2

I have JSON data that I need to parse from C# object.

this is JSON Example.

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

Here are my C# classes that are meant to contain that data:

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?

2
  • 2
    Out of curiosity, are you sure that is your JSON and you haven't made any mistakes while copying it ? For some reasons I have a feeling that "tour_type", is "tour_type": Commented Dec 27, 2019 at 1:16
  • @AnuViswan, "tour_type", is correct. Commented Dec 27, 2019 at 2:29

4 Answers 4

4

Use the JsonSerializer (System.Text.Json) object.

Code:

YourClass obj = JsonSerializer.Deserialize<YourClass>(jsonString);
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for your response. I also use this code. but error.
1

Your json has a list of list of the object... but you are declaring only List of the object.

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. 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>>. You have to use object because it can either be a string or a List<List<string>>. After you update your Types class, you can successfully deserialize the json above.

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

and based on your json definition, you can access tour_type by using the following code

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.

3 Comments

Thank you for your response, But, It's not working. "Newtonsoft.Json.JsonSerializationException: 'Error converting value "tour_type" to type 'JsonDeserializationTest.Type'. Path 'types[0][0]', line 1, position 25.'"
@Jawad I know. But It's not my json string. Third party API Response it and can not change this response string.
@SHL, does this answer your question
1

The JSON payload in the provided example is formatted quite strangely, especially since it contains seemingly unnecessary array nesting. 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. 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):

  • 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? Is it possible for an entry where no such name exists?

  • What other elements can exist in the arrays within "tour_type"?

  • Is it guaranteed that the most nested array will contain just a single (string, int) pair?

Similarly, it is hard to understand what the example C# class is trying to encapsulate. Is List<Dictionary<string, int>> necessary?

All that said, here's a solution using the built-in System.Text.Json library, that could work for you. You could write something similar using Newtonsoft.Json, if necessary. 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)

  • We can only make minimal changes to the C# class object provided in the example

The solution creates and a JsonConverter<T> that uses the low-level Utf8JsonReader to manually parse and create the custom object. 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.

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.

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):

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

Hope that helps.

Comments

0

I couldn't figure out your JSON so I created an example with verified JSON.

Try this:

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);
            }
        }
    }
}

2 Comments

Thank you for your response. but, I never change json string. It's not mine. It's Third party API Response. so I can't not change.
@SHL let me know how it turns out

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.