Configure JSON.NET to ignore DataContract/DataMember attributes
Asked Answered
P

6

47

We are running into a situation on an MVC3 project with both the Microsoft JSON serializers and JSON.NET.

Everybody knows DateTime's are basically broken in Microsoft's serializers, so we switched to JSON.NET to avoid this issue. That works great, except that some of the classes we are trying to serialize are POCOs with DataContract/DataMember attributes. They are defined in an assembly that is referenced in multiple places. Additionally, they have some other display properties that are not marked as DataMembers for efficiency. For instance, a Customer

[DataContract]
public class Customer
{
   [DataMember]
   public string FirstName { get; set;}
   [DataMember]
   public string LastName { get; set;}
   public string FullName 
   {
       get
       {  return FirstName + " " + LastName; }
   }

}

When this customer is passed over WCF the client side can reference that assembly and use the FullName just fine, but when serialized with JSON.NET it sees that FullName isn't a [DataMember] and doesn't serialize it. Is there an option to pass to JSON.NET to tell it to ignore the fact that a class has [DataContract] attribute applied?

Note: Using the JavaScriptSerializer in .NET works fine for the FullName property, but DateTimes are broken. I need JSON.NET to ignore the fact that this class has DataContract/DataMember attributes and just do standard public field serialization like it would if they weren't there.

Preiser answered 15/6, 2012 at 17:7 Comment(2)
Did you resolve this? I am having the exact same problem and need to find a resolutionDenman
I ended up adding the JsonProperty attribute for Json.NetPreiser
B
42

Simply use Json.Net's OptOut attribute. It will take precedence over DataContract.

[DataContract]
[JsonObject(MemberSerialization.OptOut)]
Biquadratic answered 11/5, 2014 at 12:29 Comment(3)
Thank you so very much. This is should be marked as the correct answer. I was working on a very old application (WCF with RESTFul api) and wanted to serialize some data to pass it to a newer web api service. this solution worked like a charm. Up voting.Scholarship
Thanks. This solved my problem very well in a big-hammer sort of way. I can see using the [JsonProperty] attribute in the next answer for more granularity.Non
Unfortunately, if you also want opt-in serialization for JSON, a [DataMember] attribute will cause the property to also be serialized to JSON. If this is unwanted, you have to add [JsonIgnore] to every property with a [DataMember] attribute, or use one of the solutions involving a custom contract resolver.Clownery
U
25

As Amry said you can uses your own IContractResolver.

Unfortunately the solution provided by Amry didn't work for me, below is the solution that I managed to get working:

public class AllPropertiesResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        //property.HasMemberAttribute = true;
        property.Ignored = false;

        //property.ShouldSerialize = instance =>
        //{
        //    return true;
        //};

        return property;
    }
}

There are a few lines commented, these wern't required to make my solution work, but you never know!

This has the same usage as Amry's solution:

var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings {
    ContractResolver = new AllPropertiesResolver()
});

Hope this helps!

Unipod answered 17/2, 2014 at 18:2 Comment(0)
H
15

I was having a problem almost related to what you're having, and managed to find a solution by going through Json.NET's codes. So it may not be the best solution, but it works for me.

To do this, you need to implement your own IContractResolver. An over-simplified implementation of that to include all parameters and ignores all attributes (not just DataContract but other built-in Json.NET's rules as well, so whatever options you set that should originally affect the members selection is now being overidden by this code):

class AllPropertiesResolver : DefaultContractResolver
{
    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        return objectType.GetProperties()
            .Where(p => p.GetIndexParameters().Length == 0)
            .Cast<MemberInfo>()
            .ToList();
    }
}

And here comes the code usage example:

var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings {
    ContractResolver = new AllPropertiesResolver()
});
Hospitality answered 28/7, 2012 at 3:9 Comment(3)
Great. Why not? What isn't working? Help us help you.Hyman
This approach did not work for me either Amry, is there anything else you had to add to get this to work?Apostrophe
This answer was working for me for the Json.NET's version way back in 2012. I don't know whether it will work now in year 2017 (seems like it does not work anymore), and regretfully I don't have the personal need to use it now in 2017, so I won't be suggesting a workable solution.Hospitality
A
10

In accordance to the Json.NET documentation [DataMember] attributes are ignored if the properties are also annotated with Json.NET specific attributes (such as [JsonProperty]).See the Serialization Attributes documentation for details:

Json.NET attributes take presidence over standard .NET serialization attributes, e.g. if both JsonPropertyAttribute and DataMemberAttribute are present on a property and both customize the name, the name from JsonPropertyAttribute will be used.

The documentation only covers the name property, but for my experience the [JsonProperty] attribute also fully shadows settings done by the [DataMember] attribute. So, if it is feasible for your case, also add Json.NET attributes to the properties for which the [DataMember] annotation should be ignored.

Abide answered 8/4, 2014 at 9:17 Comment(1)
From your answer, I learned that if a class has the [DataContract] attribute then it sets the member serialization to opt-in which means that any member (field or property) MUST have the JsonProperty or [DataMember] attribute to be serialized (otherwise it will be ignored). I spent hours to find this info.Phosphor
S
9

If you want to ignore the presence of DataContractAttribute for all types without having to add additional attributes, then a custom contract resolver is the correct solution. However, as of Json.NET 9.0.1 Amry's resolver no longer works. Doolali's resolver works but it has the additional side effect of serializing all public properties including those marked with [JsonIgnore]. If you require a contract resolver that only ignores the presence of DataContractAttribute but otherwise behaves like the default contract resolver, the following can be used:

public class IgnoreDataContractContractResolver : DefaultContractResolver
{
    static MemberSerialization RemoveDataContractAttributeMemberSerialization(Type type, MemberSerialization memberSerialization)
    {
        if (memberSerialization == MemberSerialization.OptIn)
        {
            type = Nullable.GetUnderlyingType(type) ?? type;

            // Json.NET interprets DataContractAttribute as inherited despite the fact it is marked with Inherited = false
            // https://json.codeplex.com/discussions/357850
            // https://mcmap.net/q/372382/-datacontract-and-inheritance
            // https://github.com/JamesNK/Newtonsoft.Json/issues/603
            // Thus we need to manually climb the type hierarchy to see if one is present.

            var dataContractAttribute = type.BaseTypesAndSelf().Select(t => t.GetCustomAttribute<DataContractAttribute>()).FirstOrDefault(a => a != null);
            var jsonObjectAttribute = type.GetCustomAttribute<JsonObjectAttribute>();

            if (dataContractAttribute != null && jsonObjectAttribute == null)
                memberSerialization = MemberSerialization.OptOut;
        }
        return memberSerialization;
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, RemoveDataContractAttributeMemberSerialization(type, memberSerialization));
        return properties;
    }

    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        var contract = base.CreateObjectContract(objectType);
        contract.MemberSerialization = RemoveDataContractAttributeMemberSerialization(objectType, contract.MemberSerialization);
        return contract;
    }
}

public static class TypeExtensions
{
    public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
    {
        while (type != null)
        {
            yield return type;
            type = type.BaseType;
        }
    }
}

You may want to cache the contract resolver for best performance.

Silicate answered 2/9, 2016 at 20:35 Comment(3)
@HappyNomad - DefaultContractResolver doesn't provide a simple way to inject attributes. I override CreateProperties() to pass in OptOut -- but the value of contract.MemberSerialization was still coming back as OptIn. Since that seemed inconsistent, I chose to override CreateObjectContract() as well.Silicate
Nice fix but I noticed that RemoveDataContractAttributeMemberSerialization wastefully gets called twice per type. This is because base.CreateObjectContract calls CreateProperties. I submitted an issue to JSON.NET about this.Freightage
Yea, I noticed that immediately after posting my comment and so rewrote my comment.Freightage
D
-3

Have you tried this?

IgnoreDataMemberAttribute

Danzig answered 15/6, 2012 at 17:20 Comment(1)
I want to do the opposite of ignoring the field, I want the field to be included when serializing using JSON.NETPreiser

© 2022 - 2024 — McMap. All rights reserved.