Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

partial serialization of json string: take only top level with JSON.NET

Please clarify some confusion for me: I have a JSON string that I am trying to stick into an object, but I only want to take in the top level, and treat everything underneath as a blob or string. Something like this.

Here is my model:

public class InputJson
{
    [JsonProperty(PropertyName = "signals")]
    public string signals{ get; set; }
    [JsonProperty(PropertyName = "options")]
    public string options { get; set; }
    [JsonProperty(PropertyName = "fields")]
    public string fields{ get; set; }
    [JsonProperty(PropertyName = "lapse")]
    public string lapse{ get; set; }
}

and I am trying to deserialize the JSON string (see below) to it like so:

InputJson a = JsonConvert.DeserializeObject<InputJson>(body);

I would expect that a.signals would return a string, with everything underneath "signals" and before "options", but it fails because its trying to go forward and serialize everything underneath it. I get a JsonReaderException:

Error reading string. Unexpected token: StartArray. Path 'signals', line 1, position 12.

I tried playing around with JsonSerializerSettings (like setting maxDepth to 1) but nothing helped. Anyone?

my JSON string:

{
    "signals":
    [
        {
            "name":"1",
            "att1":"44",
            "att2":"0",
            "att3":"18",
            "size":10,
            "points":[-79,-29,-9,-23,27,-110,-39,-22,-32,-2]
        },
        {
            "name":"2",
            "att1":"46",
            "att2":"0",
            "att3":"12",
            "size":10,
            "points":[36,37,37,35,38,41,41,45,39,41]
        }
    ],
    "options":"opt1 opt2",
    "fields":"myfields",
    "lapse":"somelapse"
}
like image 233
concentriq Avatar asked Oct 20 '22 06:10

concentriq


1 Answers

As you correctly surmised, you are getting this error because your field is string and the deserializer has found an array. It cannot assign an array to a string.

You can solve this problem by using a custom JsonConverter for the fields that you want treated as "blobs", by which I assume you mean you just want the raw JSON string.

Here is the code you would need for the converter:

class BlobJsonConverter: JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(string));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        return token.ToString(Formatting.None);
    }

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

Then, in your model, decorate those string properties that you want treated as "blobs" with a [JsonConverter] attribute like this:

public class InputJson
{
    [JsonProperty(PropertyName = "signals")]
    [JsonConverter(typeof(BlobJsonConverter))]
    public string signals { get; set; }

    [JsonProperty(PropertyName = "options")]
    public string options { get; set; }

    [JsonProperty(PropertyName = "fields")]
    public string fields { get; set; }

    [JsonProperty(PropertyName = "lapse")]
    public string lapse { get; set; }
}

Now when you deserialize, signals will contain the raw JSON underneath "signals" instead of giving an error.

Demo:

class Program
{
    static void Main(string[] args)
    {
        string json = @"
        {
            ""signals"":
            [
                {
                    ""name"":""1"",
                    ""att1"":""44"",
                    ""att2"":""0"",
                    ""att3"":""18"",
                    ""size"":10,
                    ""points"":[-79,-29,-9,-23,27,-110,-39,-22,-32,-2]
                },
                {
                    ""name"":""2"",
                    ""att1"":""46"",
                    ""att2"":""0"",
                    ""att3"":""12"",
                    ""size"":10,
                    ""points"":[36,37,37,35,38,41,41,45,39,41]
                }
            ],
            ""options"":""opt1 opt2"",
            ""fields"":""myfields"",
            ""lapse"":""somelapse""
        }";

        InputJson a = JsonConvert.DeserializeObject<InputJson>(json);
        Console.WriteLine("signals: " + a.signals);
        Console.WriteLine("options: " + a.options);
        Console.WriteLine("fields: " + a.fields);
        Console.WriteLine("lapse: " + a.lapse);
    }
}

Output:

signals: [{"name":"1","att1":"44","att2":"0","att3":"18","size":10,"points":[-79,-29,-9,-23,27,-110,-39,-22,-32,-2]},{"name":"2","att1":"46","att2":"0","att3":"12","size":10,"points":36,37,37,35,38,41,41,45,39,41]}]
options: opt1 opt2
fields: myfields
lapse: somelapse
like image 95
Brian Rogers Avatar answered Oct 23 '22 02:10

Brian Rogers