skip to Main Content

As a newcomer to .NET, I’m learning to work with JSON deserialization in C#. How can I efficiently deserialize a JSON array of objects with nested properties into a list of C# instances, while effectively handling the de-nesting process?

I have a JSON array representing a list of objects with nested properties, like this:

[
  {"name":"apple","properties":{"color":"red","flavor":"sweet"}},
  {"name":"orange","properties":{"color":"orange","flavor":"sour"}}
]

I want to deserialize this JSON into a list of C# objects. The C# class looks like this:

public class Fruit
{
    public string Name { get; set; }
    public string Color { get; set; }
    public string Flavor { get; set; }
}

It’s important to note that the structure of the C# object (Fruit) differs from the structure of the JSON object. In particular, the C# object does not have a "properties" variable.

Is there a more efficient way to achieve this than manually iterating through the JSON array elements and assigning values to Fruit objects?

My Code:

using System;
using System.Collections.Generic;
using System.Text.Json;

public class Fruit
{
    public string Name { get; set; }
    public string Color { get; set; }
    public string Flavor { get; set; }
}

class Program
{
    static void Main()
    {
        string json = "[{"name":"apple","properties":{"color":"red","flavor":"sweet"}},{"name":"orange","properties":{"color":"orange","flavor":"sour"}}]";

        // Deserialize into a list of Fruit objects
        List<Fruit> fruits = new List<Fruit>();

        var options = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        };

        var jsonObjects = JsonSerializer.Deserialize<List<JsonElement>>(json, options);
        
        foreach (var jsonObject in jsonObjects)
        {
            var fruit = new Fruit
            {
                Name = jsonObject.GetProperty("name").GetString(),
                Color = jsonObject.GetProperty("properties").GetProperty("color").GetString(),
                Flavor = jsonObject.GetProperty("properties").GetProperty("flavor").GetString()
            };

            fruits.Add(fruit);
        }

        // Print the fruits
        foreach (var fruit in fruits)
        {
            Console.WriteLine("Name: " + fruit.Name);
            Console.WriteLine("Color: " + fruit.Color);
            Console.WriteLine("Flavor: " + fruit.Flavor);
            Console.WriteLine();
        }
    }
}

2

Answers


  1. You have several ways of doing it. i think i would use a custom converter for this because:

    1. It’s using Utf8JsonReader (more efficient in memory, especially in large files)
    2. It’s cleaner, your logic is encapsulated in one place

    public class FruitConverter : JsonConverter<Fruit>
    {
        public override Fruit Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            Fruit fruit = new Fruit();
    
            while (reader.Read())
            {
                if (reader.TokenType == JsonTokenType.EndObject)
                {
                    return fruit;
                }
    
                if (reader.TokenType == JsonTokenType.PropertyName)
                {
                    string propertyName = reader.GetString();
                    reader.Read();
                    switch (propertyName)
                    {
                        case "name":
                            fruit.Name = reader.GetString();
                            break;
                        case "properties":
                            while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
                            {
                                if (reader.TokenType == JsonTokenType.PropertyName)
                                {
                                    string prop = reader.GetString();
                                    reader.Read();
                                    if (prop == "color")
                                        fruit.Color = reader.GetString();
                                    else if (prop == "flavor")
                                        fruit.Flavor = reader.GetString();
                                }
                            }
                            break;
                    }
                }
            }
            throw new Exception("json is not fotmatted well");
        }
    
        public override void Write(Utf8JsonWriter writer, Fruit value, JsonSerializerOptions options)
        {
            writer.WriteStartObject();
            writer.WriteString("name", value.Name);
            writer.WriteStartObject("properties");
            writer.WriteString("color", value.Color);
            writer.WriteString("flavor", value.Flavor);
            writer.WriteEndObject();
            writer.WriteEndObject();
        }
    }
    
    Login or Signup to reply.
  2. The shortest way (in one code line)

    using System.Text.Json.Nodes;
    
    List<Fruit> fruits = JsonNode.Parse(json)?.AsArray().Select(j => new Fruit { Name = (string)j["name"], Color = (string)j["properties"]["color"], Flavor = (string)j["properties"]["flavor"], })
                                .ToList();
    // or 
    
    using Newtonsoft.Json;
                                
    List<Fruit> fruits = JArray.Parse(json)
                            .Select(j => new Fruit {Name= (string)j["name"], Color= (string)j["properties"]["color"],Flavor= (string)j["properties"]["flavor"], } )
                            .ToList();
    

    or you can wrap this code in a converter

    var fruits = JsonConvert.DeserializeObject<List<Fruit>>(json, new FruitConverter());
    
    public class FruitConverter : JsonConverter<List<Fruit>>
    {
        public override List<Fruit> ReadJson(JsonReader reader, Type objectType, List<Fruit> existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            var jArr = JArray.Load(reader);
    
            return jArr.Select(j => new Fruit { Name = (string)j["name"], Color = (string)j["properties"]["color"], Flavor = (string)j["properties"]["flavor"], })
                       .ToList();
        }
        public override bool CanWrite
        {
            get { return false; }
        }
    
        public override void WriteJson(JsonWriter writer, List<Fruit> value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search