Search code examples
c#jsonjsonconverter

How do I run a converter within another converter to handle multi-format content in a complex JSON file in C#?


I am trying to deserialize a JSON file with a complex structure in C#, but I'm facing difficulties due to multi-format content within arrays. Unfortunately, I cannot change the JSON format. Here's an example of the JSON file:

{
    "messages": [
        {
            "date": "2023-05-19T17:58:17.329Z",
            "message": {
                "alert": {
                    "type": "info",
                    "message": [
                        {
                            "name": "AshesAppTest1",
                            "argType": "nonAvatarPlayer"
                        },
                        " has connected to the game server"
                    ]
                }
            },
            "activePlayer": null
        },
        {
            "date": "2023-05-19T18:56:32.304Z",
            "message": [
                {
                    "name": "AshesAppTest2",
                    "argType": "player"
                },
                " ",
                "Hello world!"
            ]
        }
    ]
}

To handle this complex JSON structure, I've created the following C# class structure and a custom converter:

public class GamePlayMessage
{
    public string date { get; set; }
    public GamePlayPlayer activePlayer { get; set; }

    [JsonProperty("message")]
    [JsonConverter(typeof(GamePlayMessageDataConverter))]
    public GamePlayMessageData MessageData { get; set; }
}

public class GamePlayMessageData
{
    [JsonProperty("alert")]
    public GamePlayComplexMessage ComplexMessage { get; set; }
}

public class GamePlayComplexMessage
{
    public string type;
    public List<object> message;
}

class GamePlayMessageDataConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(GamePlayMessageData));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Object)
        {
            return token.ToObject<GamePlayMessageData>();
        }
        else if (token.Type == JTokenType.Array)
        {
            Debug.Log(token);
            return new GamePlayMessageData
            {
                ComplexMessage = new GamePlayComplexMessage
                {
                    type = "noType",
                    message = new List<object>(token.ToArray<object>())
                }
            };
        }
        return null;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

The above code works fine until the line message = new List(token.ToArray()), where I need to utilize another converter. I'm not sure how to run a converter within another converter. Could you please assist me in achieving this? Thank you in advance for your help.


Solution

  • I would create code like this

    var data = JsonConvert.DeserializeObject<Data>(json);
    
    class GamePlayMessageDataConverter : JsonConverter<GamePlayMessageData>
    {
        public override GamePlayMessageData ReadJson(JsonReader reader, Type objectType, GamePlayMessageData existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            JToken token = JToken.Load(reader);
    
            GamePlayMessageData data = null;
    
            if (token.Type == JTokenType.Object)
            {
                data = token.ToObject<GamePlayMessageData>();
            }
            else if (token.Type == JTokenType.Array)
            {
                data = new GamePlayMessageData
                {
                    ComplexMessage = new GamePlayComplexMessage
                    {
                        type = "noType",
                        message = token.ToObject<List<object>>()
                    }
                };
            }
    
            if (data != null)
                for (int i = 0; i < data.ComplexMessage.message.Count; i++)
                {
                    if (data.ComplexMessage.message[i].GetType().ToString() == 
                                                  "Newtonsoft.Json.Linq.JObject")
                        data.ComplexMessage.message[i] = 
                          ((JObject)data.ComplexMessage.message[i]).ToObject<Arg>();
                }
    
            return data;
        }
    
        public override void WriteJson(JsonWriter writer, GamePlayMessageData value, JsonSerializer serializer)
        {
            serializer.Serialize(writer, value);
        }
    }
    

    test

    Console.WriteLine(data.Messages[0].MessageData.ComplexMessage.message[0]
                       .GetType().ToString()); // "Arg"
    

    classes

    public partial class Data
    {
        [JsonProperty("messages")]
        public List<GamePlayMessage> Messages { get; set; }
    }
    
    public class GamePlayMessage
    {
        public string date { get; set; }
        public GamePlayPlayer activePlayer { get; set; }
    
        [JsonProperty("message")]
        [JsonConverter(typeof(GamePlayMessageDataConverter))]
        public GamePlayMessageData MessageData { get; set; }
    }
    
    public class GamePlayMessageData
    {
        [JsonProperty("alert")]
        public GamePlayComplexMessage ComplexMessage { get; set; }
    }
    
    public class GamePlayComplexMessage
    {
        public string type;
        public List<object> message;
    }
    
    public partial class Arg
    {
        [JsonProperty("name")]
        public string Name { get; set; }
    
        [JsonProperty("argType")]
        public string ArgType { get; set; }
    }