3

I have a JSON string in my project like: (comes from an API call) (I also search first but nothing found that match my situation)

"
{
  "property": [
     {
       "property_1": 1,
       "property_2": true,
       "property_3": [
         {
           "property_3_1": 11,
           "property_3_2": "value_3_2_1",
           "property_3_3": true
         },
         {
           "property_3_1": 12,
           "property_3_2": "value_3_2_2",
           "property_3_3": false
         }
       ]
     },
     {
       "property_1": 2,
       "property_2": false,
       "property_3": [
         {
           "property_3_1": 21,
           "property_3_2": "value_3_2_2",
           "property_3_3": false
         },
         {
           "property_3_1": 22,
           "property_3_2": "value_3_2_2",
           "property_3_3": true
         }
       ]
     }
  ]
}
"

I need to deserialize this to list of below class:

public class PropertyDto
{
    public long Property_1 { get; set; }

    public bool property_2 { get; set; }

    public string property_3 { get; set; }
}

Is there any way to deserialize array of "property_3" as string? something like:

var peroperty = new PropertyDto();

peroperty.Peroperty_3 = "
[
  {
    "property_3_1": 21,
    "property_3_2": "value_3_2",
    "property_3_3": false
  },
  {
    "property_3_1": 12,
    "property_3_2": "value_3_2_2",
    "property_3_3": false
  }
]
"

Asp.Net Core 5 | System.Text.Json

  • So you want to deserialize it straight from the json to a `string[]`? – Dominik Mar 03 '21 at 08:50
  • @Dominik I want to deserialize only "property_3" as string not as related class structure in C# – Seyed Arman Fatemi Mar 03 '21 at 08:53
  • It's not clear to me how you intend this to work. You have "property_1", which contains an array of objects, the child objects' "property_3" property also contains an array of objects, and you seem to want to somehow flatten that to the C# model you described above. Can you tell us how (in human terms) that should work? And should `Property_1Dto` be a collection of `Property_1Dto` or just 1 object? – ProgrammingLlama Mar 03 '21 at 08:53
  • 1
    @SeyedArmanFatemi You could write a custom converter for your use case: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0 – Dominik Mar 03 '21 at 08:54
  • @John the result should be collection of Property_1Dto. I need to store the content of property_3 as string in each item – Seyed Arman Fatemi Mar 03 '21 at 08:59
  • @John I edit the example to have more clear description about problem. it was my naming fault. thank you – Seyed Arman Fatemi Mar 03 '21 at 09:05
  • Could you please provide a **valid** json as a sample? – Peter Csala Mar 03 '21 at 09:24
  • @PeterCsala I edit the question now the provided JSON is valid – Seyed Arman Fatemi Mar 03 '21 at 09:37
  • Have you checked [this solution](https://stackoverflow.com/questions/61553962/getting-nested-properties-with-system-text-json)? – Peter Csala Mar 03 '21 at 10:01
  • @PeterCsala thanks for your suggestion. this solution provide a check mechanism inside nodes / objects that does not make sense to map it to a class. My problem is saving this nodes / object (that does not make sense or we do not want map to a class) as string in my class not to manipulating theme – Seyed Arman Fatemi Mar 03 '21 at 10:37

1 Answers1

3

You can achieve this with the following JsonConverter:

public class PropertyDtoConverter : JsonConverter<PropertyDto>
{
    public override PropertyDto? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType != JsonTokenType.StartObject)
        {
            throw new JsonException();
        }

        var dto = new PropertyDto();
        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.EndObject)
            {
                return dto;
            }

            if (reader.TokenType == JsonTokenType.PropertyName)
            {
                string propName = (reader.GetString() ?? "").ToLower();
                reader.Read();

                switch (propName)
                {
                    case var _ when propName.Equals(nameof(PropertyDto.Property_1).ToLower()):
                        dto.Property_1 = reader.GetInt64();
                        break;
                    case var _ when propName.Equals(nameof(PropertyDto.Property_2).ToLower()):
                        dto.Property_2 = reader.GetBoolean();
                        break;
                    case var _ when propName.Equals(nameof(PropertyDto.Property_3).ToLower()):
                        if (JsonDocument.TryParseValue(ref reader, out JsonDocument jsonDoc))
                        {
                            dto.Property_3 = jsonDoc.RootElement.GetRawText();
                        }

                        break;
                }
            }
        }

        throw new JsonException();
    }

    public override void Write(Utf8JsonWriter writer, PropertyDto value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();

        writer.WriteNumber(nameof(PropertyDto.Property_1), value.Property_1);
        writer.WriteBoolean(nameof(PropertyDto.Property_2), value.Property_2);
        writer.WriteString(nameof(PropertyDto.Property_3), value.Property_3);

        writer.WriteEndObject();
    }
}

The model class will become:

[JsonConverter(typeof(PropertyDtoConverter))]
public class PropertyDto
{
    public long Property_1 { get; set; }

    public bool Property_2 { get; set; }
    
    public string Property_3 { get; set; }
}

You can convert string to PropertyDto explicitly:

PropertyDto dto = JsonSerializer.Deserialize<PropertyDto>(dtoString);

Or implicitly in controller action:

[HttpPost]
public void Post(PropertyDto dto)
{
    ...
}

If you want to deserialize the whole collection of PropertyDto, you can do something like this:

public class PropertyCollectionDto
{
    public List<PropertyDto> Property { get; set; } = new();
}

[HttpPost("/collection")]
public void Post(PropertyCollectionDto dto)
{
   ...
}
Ruslan Gilmutdinov
  • 1,217
  • 2
  • 9
  • 20