skip to Main Content

I’m trying to implement JSON serialization for an Optional wrapper class. I want the serialization to be akin to a nullable type, so:

"myOptional": null

for null, and

"myOptional": 2

or

"myOptional": { . . . }

for objects.

This is fine until I get to a wrapped object, I don’t know how to tell the serializer to "serialize this object like you normally would". The Microsoft docs lead me to believe that I have to implement JsonConverterFactory, but I don’t know how to tell the writer to serialize the inner object automatically.

public class OptionalTypeJsonConverterFactory : JsonConverterFactory
{
    public override bool CanConvert(Type typeToConvert)
    {
        return typeToConvert
            .GetGenericArguments()[0].IsClass;
    }

    public override JsonConverter CreateConverter(
        Type type,
        JsonSerializerOptions options
    )
    {
        return (JsonConverter)Activator.CreateInstance(
            type: typeof(OptionalJsonConverter<>).MakeGenericType(type.GenericTypeArguments[0]),
            bindingAttr: BindingFlags.Instance | BindingFlags.Public,
            binder: null,
            args: new object[] { options },
            culture: null
        )!;
    }

    private class OptionalJsonConverter<T> : JsonConverter<Optional<T>>
        where T : struct
    {
        public OptionalJsonConverter(JsonSerializerOptions options) { }

        public override Optional<T>? Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options
        )
        {
            throw new NotImplementedException();
        }

        public override void Write(
            Utf8JsonWriter writer,
            Optional<T> optional,
            JsonSerializerOptions options
        )
        {
            if (optional.IsNone())
            {
                writer.WriteNullValue();
            }
            else
            {
                // don't know what to put here. I want to serialize objects as if the wrapper was never there.
            }
        }
    }
}

How should I go about achieving this?
As an example, if Optional<T> has T = MyClass and MyClass is

{
  public int ID { get; set; } = 2
  public string Name { get; set; } = "myname"
}

then I want this to serialize to

"optionalofMyClass": 
{
  "ID": 2,
  "Name": "myname"
}

2

Answers


  1. Chosen as BEST ANSWER

    I found a solution by doing the below.

    Adding this method to Optional<T>

    private T? _value { get; }
    . . . 
    internal string Serialize()
    {
      return System.Text.Json.JsonSerializer.Serialize(_value);
    }
    

    and in the OptionalTypeJsonConverterFactory, I changed

            public override void Write(
                Utf8JsonWriter writer,
                Optional<T> optional,
                JsonSerializerOptions options
            )
            {
                if (optional.IsNone())
                {
                    writer.WriteNullValue();
                }
                else
                {
                    // don't know what to put here. I want to serialize objects as if the wrapper was never there.
                }
            }
    

    to

            public override void Write(
                Utf8JsonWriter writer,
                Optional<T> optional,
                JsonSerializerOptions options
            )
            {
                if (optional.IsNone())
                {
                    writer.WriteNullValue();
                }
                else
                {
                    writer.WriteRawValue(optional.Serialize());
                }
            }
    

    this essentially skips serializing Optional<T> and directly serializes its _value

    This works for ASP.NET endpoint serialization which was the problem I was looking to solve.


  2. If i understand your question, you want to serialize your object whether or not it has a value. If its not having a value, it should show as null, and when it does, it should the nested json structure. I tried with below code

    using System;
    using System.Text.Json;
    using System.Text.Json.Serialization;
    
    public class Program
    {
        public static void Main()
        {
            Person p1 = new Person {Id = 1, Name = "User1"};
            var output1 = new Wrapper<Person>(p1);
            Console.WriteLine(JsonSerializer.Serialize(output1));
            
            Person p2 = new Person();
            var output2 = new Wrapper<Person>(p2);
            Console.WriteLine(JsonSerializer.Serialize(output2));
            
            Person p3 = null;
            var output3 = new Wrapper<Person>(p3);
            Console.WriteLine(JsonSerializer.Serialize(output3));
        }
    }
    
    public class Wrapper<T> where T : class {
        public T optionalOfMyClass { get; set; }
        public Wrapper(T data) {
            optionalOfMyClass = data;
        }
    }
    
    public class Person {
        public int Id { get; set; }
    
        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
        public string Name { get; set; }
    }
    

    The output would look like:

    {"optionalOfMyClass":{"Id":1,"Name":"User1"}}
    {"optionalOfMyClass":{"Id":0}}
    {"optionalOfMyClass":null}
    

    Find the dotnet fiddle here.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search