1

I'm using .netcore 3.1 and I'm using System.Text.Json for serialization and deserialization. I didn't know how to phrase my question precisely. I looked around but couldn't find a direct answer for my question.

Apologies if it's a duplicate.

This is a sample JSON response.

{
    "properties": {
        "subscriptionId": "sub1",
        "usageStartTime": "2015-03-03T00:00:00+00:00",
        "usageEndTime": "2015-03-04T00:00:00+00:00",
        "instanceData": "{\"Microsoft.Resources\":{\"resourceUri\":\"resourceUri1\",\"location\":\"Alaska\",\"tags\":null,\"additionalInfo\":null}}",
        "quantity": 2.4000000000,
        "meterId": "meterID1"
    }
}

I'm interested in directly parsing instanceData. If you observe closely, instanceData is an embedded JSON string.

{
    "Microsoft.Resources": {
        "resourceUri": "resourceUri1",
        "location": "Alaska",
        "tags": null,
        "additionalInfo": null
    }
}

Question:

Is it possible to parse this instanceData while the whole Json is being parsed? Can we add some Attributes to instanceData field for direct parsing? Right now, I'm accessing the string from the parsed model class and parsing instanceData separately.

This is what I'm doing right now (something like this):

JsonSerializer.Deserialize<MicrosoftResources>(parsedResponse.instanceData).

I have already built model classes for instanceData and other entities. Currently, instanceData is of type string in my root model class.

Adithya Upadhya
  • 2,239
  • 20
  • 28
  • There are a couple of similar questions, like [this](https://stackoverflow.com/questions/59568647/system-text-json-deserialize-nested-object-from-api-call-data-is-wrapped-in-pa) or [this](https://stackoverflow.com/questions/60401444/system-text-json-deserialize-nested-object-as-string). You approach seems to be acceptable as well, just try to change type of `instanceData ` from `string` to `MicrosoftResources` – Pavel Anikhouski Apr 22 '20 at 21:13
  • @PavelAnikhouski changing type to `MicrosoftResource` is the first thing I tried. The deserialization failed that's why I posted this question. The failure is expected since from the deserializer's perspective, `instanceData` is a String not a JSON object. – Adithya Upadhya Apr 22 '20 at 23:41

2 Answers2

1

I'm interested in directly parsing instanceData. If you observe closely, instanceData is an embedded JSON string

Is it possible to parse this instanceData while the whole Json is being parsed?

You can achieve above requirement by creating and using a custom converter, like below.

public class ResConverter : JsonConverter<InstanceData>
{
    public override InstanceData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        //you can implement it based on your actual requirement
        //...

        string jsonData = reader.GetString();

        var instanceData = System.Text.Json.JsonSerializer.Deserialize<InstanceData>(jsonData);

        return instanceData;
    }

Model Classes

public class MyModel
{
    public Properties Properties { get; set; }
}

public class Properties
{
    public string SubscriptionId { get; set; }
    public DateTimeOffset UsageStartTime { get; set; }
    public DateTimeOffset UsageEndTime { get; set; }

    [JsonConverter(typeof(ResConverter))]
    public InstanceData InstanceData { get; set; }
    public double Quantity { get; set; }
    public string MeterId { get; set; }
}
    
public class InstanceData
{
    [JsonPropertyName("Microsoft.Resources")]
    public MicrosoftResources MicrosoftResources { get; set; }
}

public class MicrosoftResources
{
    public string ResourceUri { get; set; }
    public string Location { get; set; }
    public object Tags { get; set; }
    public object AdditionalInfo { get; set; }
}

Test code and result

var jsondata = "{\"Properties\":{\"SubscriptionId\":\"sub1\",\"UsageStartTime\":\"2015-03-03T00:00:00+00:00\",\"UsageEndTime\":\"2015-03-04T00:00:00+00:00\",\"InstanceData\":\"{\\u0022Microsoft.Resources\\u0022:{\\u0022ResourceUri\\u0022:\\u0022resourceUri1\\u0022,\\u0022Location\\u0022:\\u0022Alaska\\u0022,\\u0022Tags\\u0022:null,\\u0022AdditionalInfo\\u0022:null}}\",\"Quantity\":2.4,\"MeterId\":\"meterID1\"}}";

MyModel myModel = System.Text.Json.JsonSerializer.Deserialize<MyModel>(jsondata);

enter image description here

Community
  • 1
  • 1
Fei Han
  • 26,415
  • 1
  • 30
  • 41
  • The custom converter solution is basically doing what I'm doing right now with "lots of extra steps". Here's what I'm doing : `JsonSerializer.Deserialize(parsedModel.InstanceData)`. While your solution works, it's an engineering over-kill and I wouldn't recommend it. Therefore, I will upvote your answer but I cannot mark it accepted since there's no straightforward way of doing this. Even [Jon Skeet](https://stackoverflow.com/questions/26796081/parse-nested-json-string-in-c-sharp/26796968?noredirect=1#comment108583417_26796968), the .NET guru mentions there's no solution – Adithya Upadhya Apr 24 '20 at 17:52
-1

If you can change the instanceData to Json instead of string like this.

{
    "properties": {
        "subscriptionId": "sub1",
        "usageStartTime": "2015-03-03T00:00:00+00:00",
        "usageEndTime": "2015-03-04T00:00:00+00:00",
        "instanceData": {"Microsoft.Resources":{"resourceUri":"resourceUri1","location":"Alaska","tags":null,"additionalInfo":null}},
        "quantity": 2.4000000000,
        "meterId": "meterID1"
    }
}

You can easily deserialize your Json using the example below.

Model Classes:

public partial class Properties
{
    [JsonPropertyName("properties")]
    public PropertiesClass PropertiesProperties { get; set; }
}

public partial class PropertiesClass
{
    [JsonPropertyName("subscriptionId")]
    public string SubscriptionId { get; set; }

    [JsonPropertyName("usageStartTime")]
    public DateTimeOffset UsageStartTime { get; set; }

    [JsonPropertyName("usageEndTime")]
    public DateTimeOffset UsageEndTime { get; set; }

    [JsonPropertyName("instanceData")]
    public InstanceData InstanceData { get; set; }

    [JsonPropertyName("quantity")]
    public double Quantity { get; set; }

    [JsonPropertyName("meterId")]
    public string MeterId { get; set; }
}

public partial class InstanceData
{
    [JsonPropertyName("Microsoft.Resources")]
    public MicrosoftResources MicrosoftResources { get; set; }
}

public partial class MicrosoftResources
{
    [JsonPropertyName("resourceUri")]
    public string ResourceUri { get; set; }

    [JsonPropertyName("location")]
    public string Location { get; set; }

    [JsonPropertyName("tags")]
    public object Tags { get; set; }

    [JsonPropertyName("additionalInfo")]
    public object AdditionalInfo { get; set; }
}

Usage:

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

class Program
{
    static void Main(string[] args)
    {
        // escaped version, just for demo
        var json =
            "{\r\n    \"properties\": {\r\n        \"subscriptionId\": \"sub1\",\r\n        \"usageStartTime\": \"2015-03-03T00:00:00+00:00\",\r\n        \"usageEndTime\": \"2015-03-04T00:00:00+00:00\",\r\n        \"instanceData\": {\"Microsoft.Resources\":{\"resourceUri\":\"resourceUri1\",\"location\":\"Alaska\",\"tags\":null,\"additionalInfo\":null}},\r\n        \"quantity\": 2.4000000000,\r\n        \"meterId\": \"meterID1\"\r\n    }\r\n}";
        var props = JsonSerializer.Deserialize<Properties>(json);

    }
}

Props will have all of the data. I hope this helps.

Muhammad Hannan
  • 2,389
  • 19
  • 28
  • If I had the transcendental power to change the JSON response of an external Microsoft API, I wouldn't have posted this question in the first place. Would I? – Adithya Upadhya Apr 24 '20 at 17:55