Configure JSON.NET to ignore DataContract/DataMember attributes

29,036

Solution 1

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

[DataContract]
[JsonObject(MemberSerialization.OptOut)]

Solution 2

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!

Solution 3

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()
});

Solution 4

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.

Solution 5

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://stackoverflow.com/questions/8555089/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.

Share:
29,036
Nick
Author by

Nick

Software Developer for medium sized consulting firm.

Updated on July 09, 2022

Comments

  • Nick
    Nick almost 2 years

    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.

    • Kendall Bennett
      Kendall Bennett almost 12 years
      Did you resolve this? I am having the exact same problem and need to find a resolution
    • Nick
      Nick almost 12 years
      I ended up adding the JsonProperty attribute for Json.Net
  • Nick
    Nick almost 12 years
    I want to do the opposite of ignoring the field, I want the field to be included when serializing using JSON.NET
  • dbc
    dbc over 7 years
    @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.
  • HappyNomad
    HappyNomad over 7 years
    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.
  • HappyNomad
    HappyNomad over 7 years
    Yea, I noticed that immediately after posting my comment and so rewrote my comment.
  • Cornelius
    Cornelius almost 7 years
    Great. Why not? What isn't working? Help us help you.
  • Scott
    Scott over 6 years
    This approach did not work for me either Amry, is there anything else you had to add to get this to work?
  • Amry
    Amry over 6 years
    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.
  • DragoRaptor
    DragoRaptor almost 6 years
    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.
  • Wade Hatler
    Wade Hatler almost 5 years
    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.
  • stomy
    stomy over 4 years
    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.
  • Florian Winter
    Florian Winter over 3 years
    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.