Commit 40503be4 authored by Mark Canterbury's avatar Mark Canterbury
Browse files

Messing with JsonConverters

parent ad7c7e02
Loading
Loading
Loading
Loading
Loading
+51 −0
Original line number Original line Diff line number Diff line
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

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

using PolymorphicDeSerialization.Models;

namespace PolymorphicDeSerialization.CustomConverter
{
    using System.Text.Json.Serialization;

    public class HI1ObjectConverter : JsonConverter<HI1Object>
    {
        public override HI1Object Read(ref Utf8JsonReader reader,
                Type typeToConvert,
                JsonSerializerOptions options)
        {
            // Grab the type discriminator and use it to deserialise
            // to the correct derived type (without any error checking, because this is just a PoC).
            var jdoc = JsonDocument.ParseValue(ref reader);

            // Establish the correct type
            JsonElement typeVal;
            if (!jdoc.RootElement.TryGetProperty("xsiType", out typeVal)) throw new Exception("Couldn't get type of HI1Object");
            Type? t = Type.GetType(typeVal.ToString());
            if (t == null) throw new Exception($"Can't get type for {typeVal.ToString()}");
            if (!t.IsAssignableTo(typeof(HI1Object))) throw new Exception($"Can't deserialize {t.Name} as not assignable to HI1Object");

            // Make sure this converter isn't in the converters list, or
            // else this function will be called in an infinite recursion
            var dummyOptions = new JsonSerializerOptions(options);
            dummyOptions.Converters.Clear();

            // Deserialize in to the correct type
            return (HI1Object)jdoc.Deserialize(t, dummyOptions);
        }
        public override void Write(Utf8JsonWriter writer,
                HI1Object hi1Object,
                JsonSerializerOptions options)
        {
            // This forces JsonSerializer to call GetType on the instance of 
            // HI1Object and serialise it as the appropriate derived type
            // If we wanted to restrict this to specific known subtypes, we could do so
            JsonSerializer.Serialize<object>(writer, hi1Object, options);
        }
    }
}
+6 −0
Original line number Original line Diff line number Diff line
@@ -5,6 +5,12 @@
        public string ObjectIdentifier { get; set; }
        public string ObjectIdentifier { get; set; }
        public int Generation { get; set; }
        public int Generation { get; set; }


        // This serves the same purpose as the "typeDiscriminator" field.
        // I've called it xsiType because in OAS3 we can map the XSD xsiType
        // field (which is essentially XML's type discriminator) directly to JSON.
        // This just uses the .NET type name because it's just a demo and
        // I'm too lazy to actually map to the correct Qnames here.
        public string xsiType { get => this.GetType().ToString(); }
        public void ToString()
        public void ToString()
        {
        {
            Console.WriteLine(this.GetType().Name);
            Console.WriteLine(this.GetType().Name);
+16 −2
Original line number Original line Diff line number Diff line
@@ -2,6 +2,8 @@
using PolymorphicDeSerialization.Models;
using PolymorphicDeSerialization.Models;
using System.Text.Json;
using System.Text.Json;


using PolymorphicDeSerialization.CustomConverter;

// Make an LI Task Object
// Make an LI Task Object
var lit = new LITaskObject
var lit = new LITaskObject
{
{
@@ -26,13 +28,25 @@ PrintFooter();


// Scenario 3: Assign the derived type to a base type property of an object. This will result in loss of data when serialized.
// Scenario 3: Assign the derived type to a base type property of an object. This will result in loss of data when serialized.
PrintHeader("Now make an HI1Message. This has a property of type HI1Object as it can represent any HI1Object.", false);
PrintHeader("Now make an HI1Message. This has a property of type HI1Object as it can represent any HI1Object.", false);
PrintHeader("Only the HI1Object properties will be serialized. The fields in the derived type will be lost.");
//PrintHeader("Only the HI1Object properties will be serialized. The fields in the derived type will be lost.");
PrintHeader("Using a custom JSONConverter, we ensure that fields in the derived type are properly round-tripped.", false);
var hi1Message = new HI1Message
var hi1Message = new HI1Message
{
{
    HI1Object = lit
    HI1Object = lit
};
};
var hi1MessageString = JsonSerializer.Serialize(hi1Message);
var newSerOptions = new JsonSerializerOptions()
{
    WriteIndented = true,
    Converters =
    {
        new HI1ObjectConverter()
    }
};
var hi1MessageString = JsonSerializer.Serialize(hi1Message, newSerOptions);
Console.WriteLine(hi1MessageString);
Console.WriteLine(hi1MessageString);

HI1Message roundTrip = JsonSerializer.Deserialize<HI1Message>(hi1MessageString, newSerOptions);
Console.WriteLine($"Deserialized to {roundTrip.HI1Object.GetType().Name} with Target Identifier \"{(roundTrip.HI1Object as LITaskObject).TargetIdentifier}\"");
PrintFooter();
PrintFooter();


// Scenario 4: Create a custom serializer to handle derived types when serializing an object with properties of HI1Object and not the derived types.
// Scenario 4: Create a custom serializer to handle derived types when serializing an object with properties of HI1Object and not the derived types.