barteloma
barteloma

Reputation: 6855

How to deserialize raw json field to model using Newtonsoft?

So my json data is coming as string like following:

    { "name":"aaa", "sub": "{"x":"sss", "y":"eee"}" }
    

Sub field is a raw json string here.

My model is like following.

    class Main
    {
        public string Name { get;set;}
        public Sub Sub { get;set;}
    }

    class Sub
    {
        public string X { get;set;}
        public string Y { get;set;}
    }

I want to deserialize it like following:

var response = Encoding.UTF8.GetString(bytes); // getting data.
var jsonString = JsonConvert.Deseialize(response).ToString(); // to string.
var model = JsonConvert.Deserialize<Main>(jsonString); // error

The last step throws exception, like "string can not cast to Main" class.

Upvotes: 1

Views: 891

Answers (3)

Anu Viswan
Anu Viswan

Reputation: 18155

As mentioned in other Answers, you need a 2-Step deserialization.However, if you do not want to introduce a Custom JsonConverter, you could introduce a new property in the Main class which could assist in deserialization. For example,

class Main
{
    public string Name { get;set;}

    private string _subString;
    [JsonProperty("Sub")]
    public string SubString {
        get => _subString;
        set
        {
            _subString = value;
            Sub = JsonConvert.DeserializeObject<Sub>(_subString);
        }
    }
    
    [JsonIgnore]
    public Sub Sub { get;set;}
}

class Sub
{
    public string X { get;set;}
    public string Y { get;set;}
}

The Sub property would not be assigned during initial deserializing (step 1), instead, when the SubString property is assigned, it would make another call to Deserialize the string value and assign the Sub property.

Upvotes: 1

DavidG
DavidG

Reputation: 118937

Assuming your question has a typo and you meant to escape the double quotes, e.g.:

{ "name":"aaa", "sub": "{\"x\":\"sss\", \"y\":\"eee\"}" }

Then you can achieve this with a custom converter. For example:

public class NestedJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) => true;

    public override object? ReadJson(JsonReader reader, Type objectType, 
        object? existingValue, JsonSerializer serializer)
    {
        // Get the raw string
        var s = serializer.Deserialize<string>(reader);

        // Deserialise into the correct type
        return JsonConvert.DeserializeObject(s, objectType);
    }

    public override void WriteJson(JsonWriter writer, object? value, 
        JsonSerializer serializer)
            => throw new NotImplementedException();
}

And change your model to add the attribute:

class Main
{
    public string Name { get; set; }
    
    [JsonConverter(typeof(NestedJsonConverter))]
    public Sub Sub { get; set; }
}

Now you can simply deserialise like this:

var result = JsonConvert.DeserializeObject<Main>(jsonString);

Upvotes: 2

David
David

Reputation: 218828

It sounds like you need a 2-step deserialization. If the value of sub is a string then it's a string, not a Sub instance. That string may be a JSON representation of a Sub instance, but that's another deserialization altogether.

First deserialize into the type represented by the whole object:

class Main
{
    public string Name { get;set; }
    public string Sub { get;set; }
}

and:

var mainObj = JsonConvert.DeserializeObject<Main>(response);

Then mainObj.Sub would contain the JSON string for your next type:

class Sub
{
    public string X { get;set; }
    public string Y { get;set; }
}

Which you can deserialize the same way:

var subObj = JsonConvert.DeserializeObject<Sub>(mainObj.Sub);

Overall the process of deserializing a JSON string to an object is the same. You just have to do it twice when you have two JSON strings.

Upvotes: 0

Related Questions