1

I have a JSON that has the following form:

{
   "type": "oneOfMyTypes",
   "body": {
       //object corresponding to the type, contains some key-value pairs"
   }
}

The structure of the body object depends on the type. So, I want to read the type, check that it is one of my predefined types, switch on the type and parse the body into a different object depending on the type. Body objects can be very different and I do not want to make a "super body" object containing all possible attributes. I also want to use JSON and I do not want to use any binary formats.

Question: How can this be achieved using System.Text.Json or Utf8Json?

So far I have found JsonDocument+JsonElement and Utf8JsonReader. After the type is known, I will know the appropriate class for the body so I would like to use a simple parsing technique for the body, for example using JsonSerializer.Deserialize.

Answered here: Is polymorphic deserialization possible in System.Text.Json?

AlexB
  • 33
  • 5
  • Post your entire JSON – Kunal Mukherjee Oct 17 '20 at 14:11
  • Sure, here are two of my JSONs, I have many more:```{ "type": "tX13", "body": { "attribute_A": 10, "attribute_B": "whatever" } }```, now another JSON ```{ "type": "tZ17", "body": { "attribute_C": 15 } }``` – AlexB Oct 17 '20 at 14:21
  • Looks like a duplicate of [Is polymorphic deserialization possible in System.Text.Json?](https://stackoverflow.com/q/58074304/3744182) and/or [Is there a simple way to manually serialize/deserialize child objects in a custom converter in System.Text.Json?](https://stackoverflow.com/q/59743382/3744182), agree? – dbc Oct 17 '20 at 14:59
  • Agreed, typeDescriminator example there is exactly what I need (y) https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to#support-polymorphic-deserialization – AlexB Oct 17 '20 at 15:17
  • However, it does not need to be polymorphic. There is no need to create hierarchies when they do not fit (when derived classes have nothing in common). As I understand, TypeDiscriminator example can be easily changed to "discriminate" between unrelated classes. – AlexB Oct 17 '20 at 15:26
  • @AlexB - right, in which case the base type is `object`. And you would put the converter on your container class not your body classes. You might still want a base class `BodyContainerBase` and derived classes `BodyContainer` inheriting from it, depending on how you structure your code. So that answer might need tweaking depending on your exact c# data model. If you need help with that, please [edit] your question to include a sample (simplified) model -- i.e. a [mcve]. Or if not we could go ahead and close this as a duplicate. – dbc Oct 17 '20 at 15:36
  • 1
    Makes sense. It is fine to close it. – AlexB Oct 17 '20 at 15:57

2 Answers2

0

Assuming you are using Newtonsoft.Json:

When serializing, use the TypeNameHandling.Auto setting. This instructs the serializer to save the type of an object as well. When deserializing, a 'body' object of this type will be reconstructed.

var json = JsonConvert.SerializeObject(testResults,
    Formatting.Indented,
    new JsonSerializerSettings {
        TypeNameHandling = TypeNameHandling.Auto
    });

See also: https://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Heinz Kessler
  • 1,610
  • 11
  • 24
  • This answer is indeed good for the dynamic parsing, but it requires two things. First, having the type's value correspond to the class name, meaning I cannot use `"type":05` to match the class "Stockholder". Second, it assumes that there is no "body", and body's attributes are on the same level of hierarchy as the "type" itself. – AlexB Oct 17 '20 at 14:32
  • 1
    OP is specifically asking how to do this with [tag:system.text.json] not [tag:json.net]. – dbc Oct 17 '20 at 15:01
0

If you can use Json.net (a lot more flexible) then you can use JObject for this purpose:

//First, parse the json as a JObject
var jObj = JObject.Parse(json);

//Now switch the type
switch(jObj["type"].ToString())
{

    case "oneOfMyTypes":

        var oneType = jObj["body"].ToObject<oneOfMyTypes>();
    
        //process it as you need

        break;

    case "otherOfMyTypes":

        var otherType = jObj["body"].ToObject<otherOfMyTypes>();

        //process it...

        break;

    //...

    //unsupported type
    default:

        throw new InvalidDataException("Unrecognized type");

}
Gusman
  • 14,905
  • 2
  • 34
  • 50
  • 1
    OP is specifically asking how to do this with [tag:system.text.json] not [tag:json.net]. – dbc Oct 17 '20 at 15:02