简体   繁体   中英

Newtonsoft JSON serialize a template

I am trying to serialize a class to a JSON string. First, my actual code:

// Note that this class is inside a PCL
public class CommunicationMessage {

    public String Key { get; set; }

    public String Value { get; set; }

    public List<CommunicationMessage> Childs { get; set; }
}

This is a template which can be converted to xml which could look like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myproject>
    <communicationmessage>
        <header>
             <participantid>1</participantid>
             <deviceid>54325</deviceid>
             <devicetype>Smartphone Samsung 4500</devicetype>
             <timestamp>3456453445</timestamp>
             <location>343-5343-64353</location>
             <networkid>32</networkid>
             <messageid>4003</messageid>
        </header>
        <data>
        </data>
    </communicationmessage>
</myproject>

As you can see, the variable Key is an Xml-Element which is named eg communicationmessage .

Now U want the template beeing converted to a JSON string, but for sure I get instead of a element communicationmessage the element "Key":"communicationmessage" . Is there a way to get something for an element like "mymessage":"This is a test" where "mymessage" is the Key and "This is a test" the value?

Thanks for any help

Solution

I solved it with this code

public class CommunicationMessageJSONSerializer : JsonConverter {

    /// <summary>
    /// Used to 
    /// </summary>
    /// <param name="objectType"></param>
    /// <returns></returns>
    public override bool CanConvert(Type objectType) {
        return typeof(CommunicationMessage).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
    }

    /// <summary>
    /// Deserializes the JSON string
    /// </summary>
    /// <param name="reader"></param>
    /// <param name="objectType"></param>
    /// <param name="existingValue"></param>
    /// <param name="serializer"></param>
    /// <returns></returns>
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {

        // Load the JSON object from the reader
        JObject jsonObject = JObject.Load(reader);

        // Get the First Token
        JToken token = jsonObject.Children().First();

        // The deserialized message
        CommunicationMessage msg = ReadJSON(token);
        return msg;
    }

    /// <summary>
    /// This is the base method when deserializing a JSON string
    /// </summary>
    /// <param name="token"></param>
    /// <returns>The root CommunicationMessage</returns>
    private CommunicationMessage ReadJSON(JToken token) {

        CommunicationMessage root = new CommunicationMessage();

        if (token is JProperty) {
            if (token.First is JValue) {
                root.Key = ((JProperty)token).Name;
                root.Value = (string)((JProperty)token).Value;
            } else { 
                root.Key = ((JProperty)token).Name;

                foreach (JToken child in token.Children()) {
                    ReadRecursive(child, ref root);
                }
            }
        } else {
            foreach (JToken child in token.Children()) {
                ReadRecursive(child, ref root);
            }
        }
        return root;
    }

    /// <summary>
    /// This is the recursive method when deserializing a JSON string
    /// </summary>
    /// <param name="token"></param>
    /// <param name="root">The root of the coming messages</param>
    private void ReadRecursive(JToken token, ref CommunicationMessage root) {

        if (token is JProperty) {

            CommunicationMessage msg = new CommunicationMessage();

            if (token.First is JValue) {
                msg.Key = ((JProperty)token).Name;
                msg.Value = (string)((JProperty)token).Value;
            } else {
                msg.Key = ((JProperty)token).Name;

                foreach (JToken child in token.Children()) {
                    ReadRecursive(child, ref msg);
                }
            }
            root.Childs.Add(msg);
        } else {
            foreach (JToken child in token.Children()) {
                ReadRecursive(child, ref root);
            }
        }
    }

    /// <summary>
    /// Serializes a CommuicationMessage to a JSON string
    /// </summary>
    /// <param name="writer"></param>
    /// <param name="value"></param>
    /// <param name="serializer"></param>
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
        var msg = value as CommunicationMessage;
        WriteRecoursive(writer, msg, serializer);
    }

    /// <summary>
    /// This is the recursive method for serializing
    /// </summary>
    /// <param name="writer"></param>
    /// <param name="msg"></param>
    /// <param name="serializer"></param>
    private void WriteRecoursive(JsonWriter writer, CommunicationMessage msg, JsonSerializer serializer) {

        writer.WriteStartObject();
        writer.Formatting = Formatting.Indented;

        writer.WritePropertyName(msg.Key);

        if (msg.Childs.Count > 0) {
            writer.WriteStartArray();

            foreach (CommunicationMessage child in msg.Childs) {
                WriteRecoursive(writer, child, serializer);
            }

            writer.WriteEndArray();

        } else {
            writer.WriteValue(msg.Value);
        }

        writer.WriteEndObject();
    }
}

If you have any ideas to improve the code, let me know it. I will keep the solution updated.

Thanks everyone for your help

Other than using custom serialisation or implementing ISerializable , you can do the following. But please note that there is a drawback to this method whereby all properties need to be wrapped like this otherwise the the properties will not get serialised. This quick and dirty method is good for small classes but if you are creating more complicated classes, it will be better to implement ISerializable

 public class CommunicationMessage : Dictionary<string, object>       
 {
    //this "hack" exposes the "Child" as a List
    public List<CommunicationMessage> Childs
    {
        get {
            return (List<CommunicationMessage>)this["Childs"];
        }
        set
        {
            this["Childs"] = value;
        }
    }

    public CommunicationMessage()
    {
        this["Childs"] = new List<CommunicationMessage>();
    }
 }

Usage:

var m = new CommunicationMessage();
m["mymessage"] = "This is a test";

And the output should look like

{
    "Childs": [],
    "mymessage": "This is a test"
}

The alternative ISerializable implementation:

public class CommunicationMessage:ISerializable
{
    public String Key { get; set; }

    public String Value { get; set; }

    public List<CommunicationMessage> Childs { get; set; }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue(Key, Value);

        PropertyInfo[] pi=this.GetType().GetProperties();
        foreach(var p in pi)
        {
            if (p.Name == "Key" || p.Name == "Value")
                continue;
            info.AddValue(p.Name, p.GetValue(this));
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM