пытаясь сопоставить JObject с классом c # с помощью AutoMapper, я получаю NULL для дочерних элементов

#c# #.net #json.net #automapper

#c# #.net #json.net #automapper

Вопрос:

У меня есть приведенный ниже код, все работало отлично с использованием AutoMapper v4, но когда я обновился до версии 8, он начинает возвращать null в дочерних свойствах.

Теперь результат [{«Id»: null,»Name»:null},{«Id»:null,»Name»:null}]-TTR0

Что-то не так с моим кодом?

 using System;
using System.Collections.Generic;
using AutoMapper;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;

public class WarehouseObj{
    public Warehouse[] Whs { get; set; }
    public string Location { get; set; }
}

public class Warehouse{
    public string Id { get; set; }
    public string Name {get; set; }
}

public class Program
{
    public static void Main()       
    {
        var jsonString = @"{ 'wareh': [{
        'wid': '1234',
        'wname': 'W0986E'
        },{
        'wid': '1235',
        'wname': 'E0948T'
        }], 'lc' : 'TTR0'}";

        var config = new MapperConfiguration(cfg => { 
            
            cfg.CreateMap<JObject,WarehouseObj>()
                .ForMember("Whs", opt=>{ opt.MapFrom(jo => jo["wareh"]); })
                .ForMember("Location", opt=>{ opt.MapFrom(jo => jo["lc"]); });  
            
            cfg.CreateMap<JObject, Warehouse>()
                           .ForPath(dest => dest.Id, opt => { opt.MapFrom(src => src["wid"]); })
                           .ForPath(dest => dest.Name, opt => { opt.MapFrom(src => src["wname"]); });
        });
        
        IMapper mapper = config.CreateMapper();
        var jArray = JObject.Parse(jsonString);
        Console.WriteLine("{0}-{1}",JsonConvert.SerializeObject(mapper.Map<WarehouseObj>(jArray).Whs), mapper.Map<WarehouseObj>(jArray).Location);

    }
}
  

Комментарии:

1. Вы можете увидеть живой код здесь: dotnetfiddle.net/CGUJdA

2. Поскольку в комментариях вы указали, что мой ответ поможет вам решить проблему, пожалуйста, отметьте его как принятый, отметив V слева.

Ответ №1:

Одним из способов решения такой проблемы является десериализация вашей строки json для конкретных классов, таких как

 public class Wareh
    {
        public string wid { get; set; }
        public string wname { get; set; }
    }

    public class Root
    {
        public List<Wareh> wareh { get; set; }
        public string lc { get; set; }
    }
  

Затем вы можете определить свои карты со статически типизированными свойствами источника и назначения, и все будет работать нормально.
Для иллюстрации смотрите Код ниже.

 using AutoMapper;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;

public class WarehouseObj
{
    public Warehouse[] Whs { get; set; }
    public string Location { get; set; }
}

public class Warehouse
{
    public string Id { get; set; }
    public string Name { get; set; }
}

public class Wareh
{
    public string wid { get; set; }
    public string wname { get; set; }
}

public class Root
{
    public List<Wareh> wareh { get; set; }
    public string lc { get; set; }
}


public class Program
{
    public static void Main()
    {
        var jsonString = @"{ 'wareh': [{
    'wid': '1234',
    'wname': 'W0986E'
    },{
    'wid': '1235',
    'wname': 'E0948T'
    }], 'lc' : 'TTR0'}";

        var config = new MapperConfiguration(cfg =>
        {

            cfg.CreateMap<Root, WarehouseObj>()
            .ForMember(dest => dest.Whs, opts => opts.MapFrom(src => src.wareh))
            .ForMember(dest => dest.Location, opts => opts.MapFrom(src => src.lc));

            cfg.CreateMap<Wareh, Warehouse>()
            .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.wid))
            .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.wname));
        });

        IMapper mapper = config.CreateMapper();
        var jArray = JsonConvert.DeserializeObject<Root>(jsonString);
        var mapped = mapper.Map<WarehouseObj>(jArray);
        Console.WriteLine("{0}-{1}", JsonConvert.SerializeObject(mapper.Map<WarehouseObj>(jArray).Whs), mapper.Map<WarehouseObj>(jArray).Location);

    }
}
  

Комментарии:

1. Я исправил это с помощью решения OfirD, я не могу десериализовать его в класс, поскольку исходная структура JSON является общей.

Ответ №2:

Изменение второго сопоставления на JToken вместо JObject работает.

Поэтому вместо:

 cfg.CreateMap<JObject, Warehouse>()
  

Используйте:

 cfg.CreateMap<JToken, Warehouse>()
  
  • Также обратите внимание, что вы можете использовать ForMember вместо ForPath .

Что касается причины, я не совсем уверен. Мое текущее предположение заключается в том, что существует некоторая проблема с синтаксическим JArray анализом элементов в JObject s; даже если используется менее сложный json:

 var json = @"[{
    'wid': '1234',
    'wname': 'W0986E'
  },{
    'wid': '1235',
    'wname': 'E0948T'
  }]
";
  

следующее не работает (т. Е. Оно инициализирует Warehouse свойства с null помощью):

 var config = new MapperConfiguration(cfg =>
{
   cfg.CreateMap<JObject, Warehouse>()
      .ForMember(dest => dest.Id, opt => { opt.MapFrom(src => (string)src["wid"]); })
      .ForMember(dest => dest.Name, opt => { opt.MapFrom(src => (string)src["wname"]); });
});

IMapper mapper = config.CreateMapper();
var jArray = JArray.Parse(json);
var des = mapper.Map<Warehouse[]>(jArray);
  

Но изменение его на use JToken будет работать.

Комментарии:

1. ForPath() предлагает гораздо меньший диапазон опций, то есть Condition() , Ignore() и MapFrom() в то время ForMember() как предлагает дополнительные преобразования, конвертеры, замену null, предварительное условие и многое другое. Информация основана на версии 10.