简体   繁体   中英

Serialize poco containing object property into Json

I have a class that I need to serialize to pass to another system.

The class contains a property that is defined as an object, because the type of class the object will contain can vary at runtime.

My classes looks something like this simplified mock up;

public class MyTestXML
{
    public string String1 { get; set; }
    public string String2 { get; set; }

    [System.Xml.Serialization.XmlElementAttribute("First", typeof(MyFirstObject),
                              Form = System.Xml.Schema.XmlSchemaForm.Qualified)]
    [System.Xml.Serialization.XmlElementAttribute("Second", typeof(MySecondObject),
                              Form = System.Xml.Schema.XmlSchemaForm.Qualified)]
    public object MyObject { get; set; }
}
public class MyFirstObject
{
    public string theFirstObjectString { get; set; }
}
public class MySecondObject
{
    public string theSecondObjectString { get; set; }
}

This class serializes perfectly to xml by using the XmlElementAttribute and XmlSerializer, but when I try and serialize it to Json (using Newtonsoft Json.Net), the object is of an undefined type, and it cannot be deserialized.

Is there a way to specify the XmlElementAttribute in Json attributes to achieve the same result when serialized?

I would like to offer the use of Json for the serialised object, as it is half the size of the xml, but cannot unless I can solve the serialization of the object property issue.

Thanks in advance.

You would have to create your own custom serialization behaviour. Have a look at this answer here : https://stackoverflow.com/a/22722467/2039359 on how to implement your own JsonConverter for Json.Net

In your case you could do something like this to create your json

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            MyTestXML myTestXml = (MyTestXML) value;
            JObject jObject = JObject.FromObject(value);

            JProperty prop = jObject.Children<JProperty>().First(p=>p.Name.Contains("MyObject"));
            if (myTestXml.MyObject.GetType() == typeof (MyFirstObject))
            {
                prop.AddAfterSelf(new JProperty("First", JToken.FromObject(myTestXml.MyObject)));
                prop.Remove();
                jObject.WriteTo(writer);
            }
            else if (myTestXml.MyObject.GetType() == typeof (MySecondObject))
            {
                prop.AddAfterSelf(new JProperty("Second", JToken.FromObject(myTestXml.MyObject)));
                prop.Remove();
                jObject.WriteTo(writer);                    
            }
        }

And something like this when you are deserializing

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JObject jo = JObject.Load(reader);

            MyTestXML myTestXml = new MyTestXML();

            serializer.Populate(jo.CreateReader(), myTestXml);

            object myObject = null;
            if (jo["First"] != null)
            {
                myObject = new MyFirstObject { TheFirstObjectString = jo["First"].SelectToken(@"TheFirstObjectString").Value<string>() };
            }

            if (jo["Second"] != null)
            {
                myObject = new MySecondObject { TheSecondObjectString = jo["Second"].SelectToken(@"TheSecondObjectString").Value<string>() };
            }

            myTestXml.MyObject = myObject;
            return myTestXml;
        }

To use it you would supply your JsonConverter when serializing/deserializing like so:

        var settings = new JsonSerializerSettings();
        settings.Converters.Add(new MyTextXmlJsonConverter());
        var a = JsonConvert.SerializeObject(myTestXml, settings);

Hope that's what you're looking for

Another alternative is to create a custom contract resolver which would allow you to detect which xml attribute is applied. You can then apply a custom JsonConverter on the property if you needed a specific output.

public class CustomContractResolver : DefaultContractResolver
{
    private readonly JsonMediaTypeFormatter formatter;

    public CustomContractResolver(JsonMediaTypeFormatter formatter)
    {
        this.formatter = formatter;
    }

    public JsonMediaTypeFormatter Formatter
    {
        [DebuggerStepThrough]
        get { return this.formatter; }
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);
        this.ConfigureProperty(member, property);
        return property;
    }

    private void ConfigureProperty(MemberInfo member, JsonProperty property)
    {
        if (Attribute.IsDefined(member, typeof(XmlElementAttribute), true))
        {
            var attribute = member.CustomAttributes.Where(x => x.AttributeType == typeof(XmlElementAttribute)).First();
            // do something with your attribute here like apply a converter
            property.Converter = new XmlAttributeJsonConverter();
        }            
    }
}

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