简体   繁体   English

如何将 json 解析为 C# 对象(类)?

[英]How can I parse json to a C# object (Class)?

I have JSON data that I need to parse from C# object.我有需要从 C# 对象解析的 JSON 数据。

this is JSON Example.这是 JSON 示例。

{
    "types":
    [
        [
            "tour_type",
            [
                ["groups",1],
                ["individual",2]
            ]
        ]
    ]
}

Here are my C# classes that are meant to contain that data:这是我的 C# 类,用于包含该数据:

using System;
using Newtonsoft.Json;

namespace JsonDeserializationTest
{
    [JsonProperty("types")]
    public class Types
    {
        [JsonProperty]
        public List<Type> Values {get;set;}
    }

    public class Type
    {
        [JsonProperty]
        public string Key {get;set;}
        [JsonProperty]
        public List<Dictionary<string, int>> Values { get; set; }
    }
}

It's not working now.它现在不工作了。 How can I fix it?我该如何解决?

Your json has a list of list of the object... but you are declaring only List of the object.您的 json 有一个对象列表列表......但您只声明了对象列表。

public class Types
{
    [JsonProperty("types")]
    public List<List<object>> Values { get; set; } 
        // ------  UPDATE: This can only be list of list of 'object' ------- \\
}

Also, you are using the JsonProperty on the class, which is not where that normally goes.此外,您正在类上使用 JsonProperty,这通常不是。 You want to use that on the property of the class.您想在类的属性上使用它。

UPDATE:更新:

You cannot use List<List<Type>> for the json you are getting, it can only be List<List<object>> .您不能将List<List<Type>>用于您获得的 json,它只能是List<List<object>> You have to use object because it can either be a string or a List<List<string>> .您必须使用 object 因为它可以是字符串或List<List<string>> After you update your Types class, you can successfully deserialize the json above.更新Types类后,就可以成功反序列化上面的 json。

    var obj = JsonConvert.DeserializeObject<Types>(json);

and based on your json definition, you can access tour_type by using the following code并根据您的 json 定义,您可以使用以下代码访问 tour_type

types.Values.First()[0].ToString()
// output: tour_type

List<List<string>> data = JsonConvert.DeserializeObject<List<List<string>>>(types.Values.First()[1].ToString())

// data[0]
    [0]: "groups"
    [1]: "1"
// data[1]
    [0]: "individual"
    [1]: "2"

Since both of the items in the types are objects, you will either have to convert them to string or a list of list of strings or whatever object they actually are.由于类型中的两个项目都是对象,因此您必须将它们转换为字符串或字符串列表或它们实际是的任何对象。

Use the JsonSerializer (System.Text.Json) object.使用JsonSerializer (System.Text.Json) 对象。

Code:代码:

YourClass obj = JsonSerializer.Deserialize<YourClass>(jsonString);

I couldn't figure out your JSON so I created an example with verified JSON.我无法弄清楚你的 JSON,所以我用经过验证的 JSON 创建了一个示例。

Try this:尝试这个:

JSON : JSON :

{
        "Items": [
                {
                        "Name": "tour",
                        "Attributes": [
                                {
                                        "Name": "groups",
                                        "Value": 1
                                },
                                {
                                        "Name": "individual",
                                        "Value": 2
                                }
                        ]
                },
                {
                        "Name": "demo",
                        "Attributes": [
                                {
                                        "Name": "this is demo",
                                        "Value": 3
                                },
                                {
                                        "Name": "design pattern",
                                        "Value": 99
                                }
                        ]
                }
        ]
}

Types foo = JsonSerializer.Deserialize<Types>(jsonString);

public class TypeAttribute
{
    public string Name { get; set; }
    public int Value { get; set; }
}

public class Type
{
    private readonly ICollection<TypeAttribute> _attributes;

    public Type()
    {
        _attributes = new Collection<TypeAttribute>();
    }

    public void AddAttributes(IEnumerable<TypeAttribute> attrs)
    {
        foreach(TypeAttribute ta in attrs)
        {
            _attributes.Add(ta);
        }
    }

    public string Name { get; set; }
    public IEnumerable<TypeAttribute> Attributes
    {
        get { return _attributes; }

        set 
        { 
            foreach(TypeAttribute ta in value)
            {
                _attributes.Add(ta);
            }
        }
    }
}

public class Types
{
    ICollection<Type> _items;

    public Types()
    {
        _items = new Collection<Type>();
    }

    public void AddItems(IEnumerable<Type> tps)
    {
        foreach (Type t in tps)
        {
            _items.Add(t);
        }
    }

    public IEnumerable<Type> Items
    {
        get { return _items; }

        set
        {
            foreach (Type t in value)
            {
                _items.Add(t);
            }
        }
    }
}

The JSON payload in the provided example is formatted quite strangely, especially since it contains seemingly unnecessary array nesting.提供的示例中的 JSON 有效负载的格式非常奇怪,特别是因为它包含看似不必要的数组嵌套。 A payload like this usually includes more nested objects (rather than a bunch of nested arrays).像这样的负载通常包含更多的嵌套对象(而不是一堆嵌套数组)。 Additionally, it has a list of (string, int) pairs, which is semantically very similar to a Dictionary<string, int> , but the payload doesn't lend itself to that.此外,它有一个(string, int)对的列表,在语义上与Dictionary<string, int>非常相似,但有效负载并不适用于此。 It would be helpful to know where it is coming from (what context) to understand how it might change.知道它来自哪里(什么上下文)以了解它可能如何变化会很有帮助。

The example JSON brings up a few questions (that you may want to ask yourself): JSON 示例提出了一些问题(您可能想问问自己):

  • Can the "types" array contain multiple entries (at its immediate nesting)? “类型”数组是否可以包含多个条目(直接嵌套)?

  • Can the "tour_type" key name appear after the array of string, int pairs? “tour_type”键名能否出现在string, int对数组之后? Is it possible for an entry where no such name exists?是否可能存在不存在此类名称的条目?

  • What other elements can exist in the arrays within "tour_type"? “tour_type”中的数组中还可以存在哪些其他元素?

  • Is it guaranteed that the most nested array will contain just a single (string, int) pair?是否保证嵌套最多的数组将只包含一个(string, int)对?

Similarly, it is hard to understand what the example C# class is trying to encapsulate.同样,很难理解示例 C# 类试图封装什么。 Is List<Dictionary<string, int>> necessary? List<Dictionary<string, int>>有必要?

All that said, here's a solution using the built-in System.Text.Json library, that could work for you .说了这么多,这里有一个使用内置System.Text.Json库的解决方案,它可以为你工作 You could write something similar using Newtonsoft.Json , if necessary.如有必要,您可以使用Newtonsoft.Json编写类似的内容。 The solution assumes:该解决方案假设:

  • We can't change the JSON payload (and that the third party API response will always returns something that is structurally similar to the example)我们无法更改 JSON 有效负载(并且第三方 API 响应将始终返回结构上与示例相似的内容)

  • We can only make minimal changes to the C# class object provided in the example我们只能对示例中提供的 C# 类对象进行最小的更改

The solution creates and a JsonConverter<T> that uses the low-level Utf8JsonReader to manually parse and create the custom object.该解决方案创建了一个JsonConverter<T> ,它使用低级Utf8JsonReader手动解析和创建自定义对象。 This is required since nested "[" are being used to delineate what should be objects rather than "{".这是必需的,因为嵌套的“[”用于描述什么应该是对象而不是“{”。 The converter is then registered by annotating the class with the attribute.然后通过使用属性注释类来注册转换器。 Now, simply call JsonSerializer.Deserialize , passing in the JSON payload.现在,只需调用JsonSerializer.Deserialize ,传入 JSON 负载。

public class Tours
{
    [JsonPropertyName("types")]
    public List<UserType> Types { get; set; }
}

// Annotate the type to register the converter to use
[JsonConverter(typeof(CustomUserTypeConverter))]
public class UserType
{
    public string Key { get; set; }
    public Dictionary<string, int> Values { get; set; }
}

// This will use the low-level reader to build up the UserType
public class CustomUserTypeConverter : JsonConverter<UserType>
{
    // Extra structural validation was done for invalid/incomplete JSON
    // which might be too strict or incorrect and hence might require adjustments.
    public override UserType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var result = new UserType();

        if (!reader.Read())
        {
            throw new JsonException("Incomplete JSON.");
        }

        if (reader.TokenType != JsonTokenType.EndArray)
        {
            result.Key = reader.GetString();

            ReadAndValidate(ref reader, JsonTokenType.StartArray);

            int depthSnapshot = reader.CurrentDepth;

            var values = new Dictionary<string, int>();

            do
            {
                reader.Read();
                if (reader.TokenType != JsonTokenType.StartArray && reader.TokenType != JsonTokenType.EndArray)
                {
                    throw new JsonException($"Invalid JSON payload. Expected Start or End Array. TokenType: {reader.TokenType}, Depth: {reader.CurrentDepth}.");
                }

                if (reader.CurrentDepth <= depthSnapshot)
                {
                    break;
                }

                reader.Read();

                if (reader.TokenType != JsonTokenType.EndArray)
                {
                    string key = reader.GetString();

                    reader.Read();
                    int value = reader.GetInt32();
                    values.Add(key, value);

                    ReadAndValidate(ref reader, JsonTokenType.EndArray);
                }

            } while (true);

            ReadAndValidate(ref reader, JsonTokenType.EndArray);

            result.Values = values;
        }

        return result;
    }

    private void ReadAndValidate(ref Utf8JsonReader reader, JsonTokenType expectedTokenType)
    {
        bool readNext = reader.Read();
        if (!readNext || reader.TokenType != expectedTokenType)
        {
            string message = readNext ?
                $"Invalid JSON payload. TokenType: {reader.TokenType}, Depth: {reader.CurrentDepth}, Expected: {expectedTokenType}" :
                $"Incomplete JSON. Expected: {expectedTokenType}";
            throw new JsonException(message);
        }
    }

    // Implement this method if you need to Serialize (i.e. write) the object
    // back to JSON
    public override void Write(Utf8JsonWriter writer, UserType value, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}

Here's how you would use the above converter to serialize the JSON string provided in the example, along with how to access the values.下面介绍如何使用上述转换器序列化示例中提供的 JSON 字符串,以及如何访问这些值。

public static Tours ParseJson(string json)
{
    Tours tours = JsonSerializer.Deserialize<Tours>(json);
    return tours;
}

public static void AccessValues(Tours tours)
{
    foreach (UserType data in tours.Types)
    {
        string typeName = data.Key; // "tour_type"

        foreach (KeyValuePair<string, int> pairs in data.Values)
        {
            string key = pairs.Key; // "groups" or "individual
            int value = pairs.Value; // 1 or 2
        }
    }
}

For what it's worth, Visual Studio suggests the following C# class structure for the example JSON (which is similar to what @Jawad suggested):就其价值而言,Visual Studio 为示例 JSON 建议了以下 C# 类结构(类似于 @Jawad 建议的内容):

public class Rootobject
{
    public object[][] types { get; set; }
}

Hope that helps.希望有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM