Search code examples
c#jsonserializerkafkaflow

How to parse KafkaFlow message using a custom serializer


KafkaFlow serialises like this

{
    "actions": {
        "$type": "Full.ActionTypeName, supplying-assembly-name",
        "$values": [
            {
                "propname": "propvalue" //properties of ActionTypeName instance
            }
        ]
    },
    "resources": {
        "$type": "Full.ResourceTypeName, supplying-assembly-name",
        "$values": [
            {
                "propname": "propvalue" //properties of ResourceTypeName instance
            }
        ]
    }
}

With the following classes

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

public class FlowMessage
{
    public FlowType Actions { get; set; }
    public FlowType Resources { get; set; }
}

public class FlowType
{
    [JsonPropertyName("$type")]
    public string TypeDescriptor { get; set; }
    [JsonPropertyName("$values")]
    public JsonElement[] Values { get; set; }
    public string TypeName() => TypeDescriptor.Split(",")[0];
    public Type Type() => System.Type.GetType(TypeName());
}

I can get this to parse.

var stream = File.Open("sample.json", FileMode.Open, FileAccess.Read);
JsonSerializerOptions seropt = new()
{
    PropertyNameCaseInsensitive = true,
    Converters = { new JsonStringEnumConverter() }
};

var foo = JsonSerializer.Deserialize(stream, typeof(FlowMessage), seropt);

This "FlowType" notation is used recursively to represent object graphs. According to the JsonSerializer documentation it is possible to associate a JsonConverter with a type. What I want to do is apply this somehow so that instead of the pair of nodes $type and $values the serialiser returns an array of objects of appropriate type.

It seems like I need to use the factory pattern converter described on that page, but I need a value from inside the FlowType instance to determine the target type for the converter that the factory will produce. So I need to parse it first as a FlowType and then again using the converter. It's really not obvious how to go about this.

Surely someone has built a deserialiser of this sort. I look forward to your advice on how to deal with this.

Update

It appears you can trivially clone the reader for the purpose of read-ahead as described elsewhere in the same document. But I'm still pretty wobbly so if someone knows what they're doing with this I wouldn't object to being led by the nose.


Solution

  • It appears this format is supported in both directions by Newtonsoft.Json if you specify serialisation options as follows.

    JsonSerializerSettings _options = new()
    {
      TypeNameHandling = TypeNameHandling.Auto,
    };
    

    I'd rather not run two serialisers so if anyone can figure out how to handle it using System.Text.Json I would accept that as the correct answer.