C# JSON de/serialize as Interface or string

Viewed 75

Using Newtonsoft.Json and given the "Greeting" class below, I'm trying to allow "Message" to be de/serialized as it does now and as a string.

public class Greeting
{
    public IHello Message { get; set; }
}

public interface IHello
{
    string Phrase { get; set; }
}

public class Hello1 : IHello
{
    public string Phrase { get; set; }
}

var greeting = new Greeting
{
    Message = new Hello1
    {
        Phrase = "Yo!"
    }
};

var jsonText = JsonConvert.SerializeObject(greeting);

Current output: {"Message":{"Phrase":"Yo!"}} but I also want to be able to output {"Message":"Yo!"}

I did consider using a Generic

public class Greeting<T>
{
    public T Message { get; set; }
}

But I didn't want the user to have to know about setting

var greeting = new Greeting<string> { Message = "" };

or

var greeting = new Greeting<Hello1> { Message = new Hello1 { Phrase = "Yo!" }};

Is it possible to achieve the desired json de/serialization in another way perhaps? Or maybe I use the implementation above but hide that away somehow?

All help is appreciated!

2 Answers

you can modify The Greeting class like this:

public interface IMessaging : IEnumerable<char>, IEnumerable, ICloneable, IComparable, IComparable<String?>, IConvertible, IEquatable<String?>, IHello
{
}
public class Greeting
{
    public Greeting SetMessage<T>(T message) where T: IMessaging
    {
        Message = message;
        return this;
    }
    
    public object Message { get; set; }
}

and then you can use it either

var greeting = new Greeting().SetMessage("Yo");
var jsonText = JsonConvert.SerializeObject(greeting);

that result {"Message":"Yo"}

or this way:

var greeting = new Greeting().SetMessage(new Hello1 { Phrase = "Yo!" });
var jsonText = JsonConvert.SerializeObject(greeting);

that results {"Message":{"Phrase":"Yo!"}} or you can add any interfaces you want to Greeting Class

Write a CustomConverter. Here I've created a custom converter which can be used with Hello1, which simply omits some data. If you instead use the class Hello2, everything is included.

public class Hello1Converter : Newtonsoft.Json.JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Hello1);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var obj = value as IHello;
        writer.WriteValue(obj.Phrase);
    }
}

And use it like this:

    [TestMethod]
    public void TestMethod1()
    {
        var greeting = new Greeting
        {
            Message = new Hello1
            {
                Phrase = "Yo!"
            }
        };

        var greeting2 = new Greeting
        {
            Message = new Hello2
            {
                Phrase = "Yo!"
            }
        };

        var jsonText1 = JsonConvert.SerializeObject(greeting, Formatting.Indented, new Hello1Converter());
        var jsonText2 = JsonConvert.SerializeObject(greeting2, Formatting.Indented, new Hello1Converter());
    }

jsonText1:

{
  "Message": "Yo!"
}

jsonText2:

{
  "Message": {
    "Phrase": "Yo!"
  }
}

Please note that this only handles the serialization. For dezerialization you need to implement ReadJson as well.

And your classes:

public class Greeting
{
    public IHello Message { get; set; }
}

public interface IHello
{
    string Phrase { get; set; }
}

public class Hello1 : IHello
{
    public string Phrase { get; set; }
}

public class Hello2 : IHello
{
    public string Phrase { get; set; }
}
Related