【问题标题】:Json Array to Custom Object Mapping in C#C# 中 Json 数组到自定义对象映射
【发布时间】:2018-05-14 15:49:33
【问题描述】:

我有这个 JSON 数组作为输入。

[
    {
        "FirstName": "Test1",
        "LastName": "Test2",
        "Address": "London, GB",
        "Error": "Something's gone wrong"
    },
    {
        "FirstName": "Test3",
        "LastName": "Test4",
        "Address": "NewYork, US",
        "Error": "Something's gone wrong"
    },
    {
        "DisplayName": "ContactNumber",
        "Value": "01234 123 123"
    }
]

我想在 C# 中构建一个这样的 JSON 对象

[
    "pages":{
        "FirstName": "Test1",
        "LastName": "Test2",
        "Address": "London, GB",
        "Error": "Something's gone wrong"
    },
    {
        "FirstName": "Test3",
        "LastName": "Test4",
        "Address": "NewYork, US",
        "Error": "Something's gone wrong"
    },
  "labels": {
        "DisplayName": "ContactNumber",
        "Value": "01234 123 123"
  }
}
]

我已经创建了一个具有上述输出属性的模型,但是当我将对象反序列化为该模型时,它们没有被映射。所有值都返回 null。

var deserializedData = JsonConvert.DeserializeObject<List<Config>>(serializedData);

我检查时收到的响应是

[
    {
        "pages": null,
        "labels": null
    },
    {
        "pages": null,
        "labels": null
    }
]

谁能帮我在C#中为我想要的这种格式构建自定义模型

提前致谢。

【问题讨论】:

  • 您想要的 JSON 无效。如果您将其上传到jsonlint.com,您将获得Error: Parse error on line 7: Expecting 'STRING', got '{'"pages""labels" 的值应该是数组吗?
  • 我已经创建了一个具有上述属性的模型,但是当我将对象反序列化到该模型中时,它们没有被映射。 -- 你能分享你到目前为止所尝试的吗?
  • 尝试使用 Visual Studio 菜单并使用它从您的 json 创建类...您可能会遇到问题,因为它不会将 json 字符串反序列化到您的结构中
  • 我想构建一个这样的 JSON 数组 [{ "Pages": { properties here}, "Labels": { properties here } }]
  • 最好提供完整的 json 字符串,您输入的函数用于转换为对象 ...

标签: c# arrays json


【解决方案1】:

您拥有的是一个多态 JSON 数组,其中包含各种类型的对象,可通过特定属性的存在来区分(例如"DisplayName""FirstName")。您想要做的是将其反序列化为 c# 模型,其中每个可能的数组项都被添加到模型的集合值属性中,其中选择集合属性以具有正确的项类型。

这可以通过使用custom JsonConverter 来完成。由于问题陈述是通用的,我将使转换器成为通用的。硬编码转换器需要更少的代码。

首先,如下定义您想要的模型:

public class Page
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }
    public string Error { get; set; }
}

public class Label
{
    public string DisplayName { get; set; }
    public string Value { get; set; }
}

public class RootObject
{
    public List<Page> pages { get; set; }
    public List<Label> labels { get; set; }
}

接下来定义以下转换器:

public class PolymorphicArrayToObjectConverter<TObject> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(TObject).IsAssignableFrom(objectType);
    }

    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    static JsonObjectContract FindContract(JObject obj, IEnumerable<Type> derivedTypes, JsonSerializer serializer)
    {
        List<JsonObjectContract> bestContracts = new List<JsonObjectContract>();
        foreach (var type in derivedTypes)
        {
            if (type.IsAbstract)
                continue;
            var contract = serializer.ContractResolver.ResolveContract(type) as JsonObjectContract;
            if (contract == null)
                continue;
            if (obj.Properties().Select(p => p.Name).Any(n => contract.Properties.GetClosestMatchProperty(n) == null))
                continue;
            if (bestContracts.Count == 0 || bestContracts[0].Properties.Count > contract.Properties.Count)
            {
                bestContracts.Clear();
                bestContracts.Add(contract);
            }
            else if (contract.Properties.Count == bestContracts[0].Properties.Count)
            {
                bestContracts.Add(contract);
            }
        }
        return bestContracts.Single();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        else if (reader.TokenType != JsonToken.StartArray)
            throw new InvalidOperationException("JSON token is not an array at path: " + reader.Path);

        var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
        existingValue = existingValue ?? contract.DefaultCreator();

        var lookup = contract
            .Properties
            .Select(p => new { Property = p, PropertyContract = serializer.ContractResolver.ResolveContract(p.PropertyType) as JsonArrayContract })
            .Where(i => i.PropertyContract != null)
            .ToDictionary(i => i.PropertyContract.CollectionItemType);
        var types = lookup.Select(i => i.Key).ToList();

        while (reader.Read())
        {
            switch (reader.TokenType)
            {
                case JsonToken.Comment:
                    break;
                case JsonToken.EndArray:
                    return existingValue;
                default:
                    {
                        var itemObj = JObject.Load(reader);
                        var itemContract = FindContract(itemObj, types, serializer);
                        if (itemContract == null)
                            continue;
                        var item = serializer.Deserialize(itemObj.CreateReader(), itemContract.UnderlyingType);
                        var propertyData = lookup[itemContract.UnderlyingType];
                        var collection = propertyData.Property.ValueProvider.GetValue(existingValue);
                        if (collection == null)
                        {
                            collection = propertyData.PropertyContract.DefaultCreator();
                            propertyData.Property.ValueProvider.SetValue(existingValue, collection);
                        }
                        collection.GetType().GetMethod("Add").Invoke(collection, new [] { item });
                    }
                    break;
            }
        }
        // Should not come here.
        throw new JsonSerializationException("Unclosed array at path: " + reader.Path);
    }
}

然后,反序列化并重新序列化您的 RootObject 集合,如下所示:

var settings = new JsonSerializerSettings
{
    Converters = { new PolymorphicArrayToObjectConverter<RootObject>() },
};
var root = new List<RootObject> { JsonConvert.DeserializeObject<RootObject>(jsonString, settings) };

var outputJson = JsonConvert.SerializeObject(root, Formatting.Indented);

因此,将生成以下 JSON:

[
  {
    "pages": [
      {
        "FirstName": "Test1",
        "LastName": "Test2",
        "Address": "London, GB",
        "Error": "Something's gone wrong"
      },
      {
        "FirstName": "Test3",
        "LastName": "Test4",
        "Address": "NewYork, US",
        "Error": "Something's gone wrong"
      }
    ],
    "labels": [
      {
        "DisplayName": "ContactNumber",
        "Value": "01234 123 123"
      }
    ]
  }
]

示例fiddle

注意 - 自动推断数组项类型的代码改编自 this answer

【讨论】:

  • 像魅力一样工作!谢谢dbc。这就是我要找的。​​span>
【解决方案2】:

你的 json 不是用于解析的有效 json 结构,这根本无效(在这里检查它有什么问题:https://jsonlint.com/

您提供的 json 字符串无效

[
    "pages":{
        "FirstName": "Test1",
        "LastName": "Test2",
        "Address": "London, GB",
        "Error": "Something's gone wrong"
    },
    {
        "FirstName": "Test3",
        "LastName": "Test4",
        "Address": "NewYork, US",
        "Error": "Something's gone wrong"
    },
  "labels": {
        "DisplayName": "ContactNumber",
        "Value": "01234 123 123"
  }
}
]

但是我修复了你给的 json

已修复且正常工作的 json

[{
    "pages": [{
            "FirstName": "Test1",
            "LastName": "Test2",
            "Address": "London, GB",
            "Error": "Something's gone wrong"
        },
        {
            "FirstName": "Test3",
            "LastName": "Test4",
            "Address": "NewYork, US",
            "Error": "Something's gone wrong"
        }
    ],
    "labels": {
        "DisplayName": "ContactNumber",
        "Value": "01234 123 123"
    }
}]

应用 fix json 后,您的 C# 对象是

public class Page
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }
    public string Error { get; set; }
}

public class Labels
{
    public string DisplayName { get; set; }
    public string Value { get; set; }
}

public class RootObject
{
    public List<Page> pages { get; set; }
    public Labels labels { get; set; }
}

我用我的固定 json 尝试了这个,它工作正常。

编辑结束


下面的代码对我来说工作正常,我在下面提供给你的类结构

对于列表

var jsonString = "[{ \"pages\": {\"id\": 12345, \"name\": \"John Doe\", \"number\": \"123\", \"test\": "+
 "\"John\" }, \"labels\": { \"test\": \"test1\", \"test2\": \"test2\" } }," +
                            "{ \"pages\": {\"id\": 12345, \"name\": \"John Doe\", \"number\": \"123\", \"test\": "+
 "\"John\" }, \"labels\": { \"test\": \"test1\", \"test2\": \"test2\" } }]";

var deserializedData = JsonConvert.DeserializeObject<List<RootObject>>(jsonString);

以单个对象作为输入

var jsonString = "{ \"pages\": {\"id\": 12345, \"name\": \"John Doe\", \"number\": \"123\", \"test\": "+
 "\"John\" }, \"labels\": { \"test\": \"test1\", \"test2\": \"test2\" } }";

 var deserializedData = JsonConvert.DeserializeObject<RootObject>(jsonString);

基于输入

{ "pages": { "id": 12345, "name": "John Doe", "number": "123", "test": 
 "John" }, "labels": { "test": "test1", "test2": "test2" } }

下面是生成的类

public class Pages
{
    public int id { get; set; }
    public string name { get; set; }
    public string number { get; set; }
    public string test { get; set; }
}

public class Labels
{
    public string test { get; set; }
    public string test2 { get; set; }
}

public class RootObject
{
    public Pages pages { get; set; }
    public Labels labels { get; set; }
}

如果你有 Json,那么你可以使用 Visual Studio 本身生成 C# 类。

在 Visual Studio 中找到“Paste Sepcial”菜单。即复制您的 json 字符串并单击 Paste special ,它将为您生成 C# 类。

你可以关注我的帖子:Generate Class From JSON or XML in Visual Studio

或使用在线网站,例如:json2csharp

【讨论】:

  • 我的输入是 [ { "FirstName": "Test1", "LastName": "Test2", "Address": "London, GB", "Error": "Something's go wrong" }, {“FirstName”:“Test3”,“LastName”:“Test4”,“Address”:“NewYork,US”,“Error”:“出了点问题”},{“DisplayName”:“ContactNumber”,“Value” : "01234 123 123" } ] 我已经使用粘贴 JSON 作为类生成了类。我仍然得到响应为 null 我提供的输入是一个 JSON 数组。我不想用 JSON 中定义的自定义
  • @EKD 请提供您的 C# 代码以执行此操作
  • var deserializedData = JsonConvert.DeserializeObject>(serializedData);输出 [ { "pages": null, "labels": null }, { "pages": null, "labels": null } ]
  • @EKD - 请也提供您的完整 json .....我正在尝试结束
  • 我已经正确更新了问题@Pranay 期待您的帮助。
猜你喜欢
  • 2018-10-31
  • 2020-02-10
  • 1970-01-01
  • 1970-01-01
  • 2015-12-23
  • 2020-09-28
  • 2015-10-09
  • 2014-01-21
  • 1970-01-01
相关资源
最近更新 更多