16

I have a .Net object which I've been serializing to Xml and is decorated with Xml attributes. I would now like to serialize the same object to Json, preferably using the Newtonsoft Json.Net library.

I'd like to go directly from the .Net object in memory to a Json string (without serializing to Xml first). I do not wish to add any Json attributes to the class, but instead would like for the Json serializer to use the existing Xml attributes.

public class world{
  [XmlIgnore]
  public int ignoreMe{ get; }

  [XmlElement("foo")]
  public int bar{ get; }

  [XmlElement("marco")]
  public int polo{ get; }
}

becomes

{
  "foo":0,
  "marco":0
}
Iain Sproat
  • 5,210
  • 11
  • 48
  • 68
  • Newtonsoft Json.Net http://james.newtonking.com/projects/json/help/SerializationAttributes.html "also looks for the DataContract and DataMember attributes when determining how JSON is to be serialized and deserialized". Does anyone know if XmlElementAttributes et al are interoperable with DataContractAttributes? – Iain Sproat Jan 14 '11 at 18:24
  • A serializer could implement serialization of both Attributes, but I guess that's up to the serializer ... (also see http://social.msdn.microsoft.com/Forums/en/wcf/thread/be796bd0-2502-4bd3-96d9-60bf7d68930d) – hangy Jan 20 '11 at 16:27
  • I've written a small patch for Json.Net which allows the DefaultContractResolver to work with Xml attributes. It works for the simple example above, but I need to write some more tests for more complex examples (AnonymousType etc.) before releasing it. – Iain Sproat Jan 20 '11 at 17:37

4 Answers4

9

Use [JsonProperty(PropertyName="foo")] Attribute and set the PropertyName.

Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • Unfortunately this doesn't answer the question. I'm reluctant to add any more attributes to my POCOs - I've hundreds of them with thousands of properties, and wish to steer away from a solution where I have two places to modify if a property name changes. – Iain Sproat Jan 14 '11 at 09:41
  • OK, so update the question with this requirement of yours so that people can contribute. BTW, I got a downvote, was it you? – Aliostad Jan 14 '11 at 10:09
  • This is a couple of months old but if you are worried about over decorating your POCOS, why not use a view model for your json attributes? I use this and my POCOS stay nice and clean. – trevorc Apr 27 '11 at 21:34
  • Thanks and helped me! – S.N Oct 06 '16 at 12:59
7

Turns out this wasn't an existing feature of the Newtonsoft Json.Net library. I've written a patch and uploaded it to the Json.Net issue tracker (archived link here):

This allows for the following:

  • XmlIgnore works just like JsonIgnore.
  • XmlElementAttribute.ElementName will alter the Json property name.
  • XmlType.AnonymousType will suppress objects from being printed to Json (XmlContractResolver.SuppressAnonymousType property alters this behaviour) this is a little bit hacky, as I've had to learn Json.Net's internals as I've been going.
dbc
  • 104,963
  • 20
  • 228
  • 340
Iain Sproat
  • 5,210
  • 11
  • 48
  • 68
  • 12
    Just in case anyone comes across this, it didn't make it into the source for Json.NET. I would be very interested to hear of any other solutions. – Martin Mar 06 '13 at 08:58
  • I personally like the idea of adding choosing which serializer you want to use, and also, potentially, what order (as James mentioned in the issue). – Dan Atkinson Feb 20 '14 at 13:47
  • I believe the archived patch is now here: https://github.com/iainsproat/ifc-dotnet/blob/master/Json.Net%20patch%202011-04-21. – dbc Jun 11 '18 at 18:08
5

You could create a custom contract resolver which would allow you to make adjustments to the properties and set them to ignore where an XmlIgnoreAttribute is set.

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(XmlIgnoreAttribute), true))
        {
            property.Ignored = true;
        }            
    }
}

You can use apply this custom resolver by setting the ContractResolver property of the JsonSerializerSettings when serializing an object

https://www.newtonsoft.com/json/help/html/ContractResolver.htm

string json =
    JsonConvert.SerializeObject(
        product, // this is your object that has xml attributes on it that you want ignored
        Formatting.Indented,
        new JsonSerializerSettings { ContractResolver = new CustomResolver() }
        );

If you're using WebApi you can set it globally to apply to all contracts.

var config = GlobalConfiguration.Configuration;
var jsonSettings = config.Formatters.JsonFormatter.SerializerSettings;
jsonSettings.ContractResolver = new CustomContractResolver();
Denis Pitcher
  • 3,080
  • 1
  • 27
  • 19
0

The class below can be used to serialize (and deserialize) parts of the object tree to XML and then to JSON.

Usage

[JsonObject]
public class ClassToSerializeWithJson
{
    [JsonProperty]
    public TypeThatIsJsonSerializable PropertySerializedWithJsonSerializer {get; set; }

    [JsonProperty]
    [JsonConverter(typeof(JsonXmlConverter<TypeThatIsXmlSerializable>))]
    public TypeThatIsXmlSerializable PropertySerializedWithCustomSerializer {get; set; }
}

JsonXmlConverter class

public class JsonXmlConverter<TType> : JsonConverter where TType : class
{
    private static readonly XmlSerializer xmlSerializer = new XmlSerializer(typeof(TType));

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var xml = ToXml(value as TType);
        using (var stream = new StringReader(xml))
        {
            var xDoc = XDocument.Load(stream);
            var json = JsonConvert.SerializeXNode(xDoc);
            writer.WriteRawValue(json);
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null) 
        { 
            // consume the 'null' token to set the reader in the correct state
            JToken.Load(reader); 
            return null; 
        }
        var jObj = JObject.Load(reader);
        var json = jObj.ToString();
        var xDoc = JsonConvert.DeserializeXNode(json);
        var xml = xDoc.ToString();
        return FromXml(xml);
    }

    public override bool CanRead => true;

    public override bool CanConvert(Type objectType) => objectType == typeof(TType);

    private static TType FromXml(string xmlString)
    {
        using (StringReader reader = new StringReader(xmlString))
            return (TType)xmlSerializer.Deserialize(reader);
    }

    private static string ToXml(TType obj)
    {
        using (StringWriter writer = new StringWriter())
        using (XmlWriter xmlWriter = XmlWriter.Create(writer))
        {
            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add(String.Empty, String.Empty);

            xmlSerializer.Serialize(xmlWriter, obj, ns);
            return writer.ToString();
        }
    }
}
JanDotNet
  • 3,746
  • 21
  • 30