43

I have a class set up as follows:

public class Foo
{
    public string string1 { get; set; }
    public string string2 { get; set; }
    public string string3 { get; set; }
}

I am using Json.Net to deserialize the following Json Response:

[
    {
        "number1": 1,
        "number2": 12345678901234567890,
        "number3": 3
    },
    {
        "number1": 9,
        "number2": 12345678901234567890,
        "number3": 8
    }
]

Deserialization code:

string json = @"[
    {
        ""number1"": 1,
        ""number2"": 12345678901234567890,
        ""number3"": 3
    },
    {
        ""number1"": 9,
        ""number2"": 12345678901234567890,
        ""number3"": 8
    }
]"

List<Foo> foos = JsonConvert.DeserializeObject<List<Foo>>(json);

The value in number2 exceeds an Int64, but I don't really care about retrieving that value. Is there a way to cast the number2 property to a string, or fully ignore it during deserialization?

I have tried adding the [JsonConverter(typeof(string))] attribute to the string2 property, but recieve the error: Error creating System.String. I have also tried setting typeof(decimal).

I have also tried using [JsonIgnore] but that doesn't work.

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
FEXTWOLF
  • 451
  • 1
  • 4
  • 5

9 Answers9

44

You can use MissingMemberHandling property of the JsonSerializerSettings object.

Example usage:

var jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore;

JsonConvert.DeserializeObject<YourClass>(jsonResponse, jsonSerializerSettings);

More info here.

Yoh Deadfall
  • 2,711
  • 7
  • 28
  • 32
Florin D. Preda
  • 1,358
  • 1
  • 11
  • 25
  • 59
    he is asking to ignore specific key not handle missing key, you answer is irrelevant to the question – Alrehamy Dec 30 '16 at 11:29
  • 1
    it's interesting still how many of us were searching for actually this answer - i.e. we had a different question, but the answer we got was right. i am almost feeling surreal – hello_earth Jun 12 '20 at 16:03
11

This is a lame workaround but you could make a method to manually load the json. If it's too much data to load without an automatic deserializer just remove the nodes that you don't want. This is a lot slower though.

public static List<Foo> FromJson(string input) {
    var json = JToken.Parse(input);
    json["key"].Remove();
    var foo = JsonConvert.DeserializeObject<List<Foo>>(json.ToString());

}

This is an interesting problem I wonder if anyone has a better solution for it.

Dharun
  • 613
  • 8
  • 26
10

Here's the Newtonsoft Json preferred way to ignore a property without having to modify the class as based on http://james.newtonking.com/json/help/index.html?topic=html/ReducingSerializedJSONSize.htm

This one is used to ignore lazy reference properties of EF or Linq2Sql

public class DynamicContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, 
        MemberSerialization memberSerialization)
    {
        Func<Type,bool> includeProperty = t => t.IsValueType || t.Namespace.StartsWith("System") && t.Namespace.StartsWith("System.Data")==false; 
        IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
        var allProperties = properties.Select (p => new{p.PropertyName,Including=includeProperty(p.PropertyType), p.PropertyType});//.Dump("props");
        var warnProperties=allProperties.Where (a =>a.Including && a.PropertyType.IsValueType==false && a.PropertyType.Name.IsIgnoreCaseMatch("String")==false) ;

        //linq pad debugging helper
        //var propertyTypesSerializing= allProperties.Where (p => p.Including).Select (p => p.PropertyType).Distinct().OrderBy (p => p.Name).Dump();

        if(warnProperties.Any())
        {
            //LinqPad helper
            //Util.Highlight(warnProperties.ToArray()).Dump("warning flag raised, aborting");
            throw new ArgumentOutOfRangeException();
        }

        properties = properties.Where(p =>includeProperty(p.PropertyType)).ToList();
        return properties;
    }
}

All the .Dump() calls are just linqpad debugging helpers, not needed method calls.

sample usage:

var inactives = from am in Aspnet_Memberships
        join mm in Member_members on am.UserId equals mm.Member_guid
        where mm.Is_active==false && mm.Org_id==1
        select new{am,mm};
        //inactives.Take(4).ToArray().Dump();
        var serialized = JsonConvert.SerializeObject(
            inactives.Skip(1).Select(i => i.mm).First(), 
            new  JsonSerializerSettings()
            {
                ContractResolver = new DynamicContractResolver(), 
                PreserveReferencesHandling = PreserveReferencesHandling.None,
                ReferenceLoopHandling= ReferenceLoopHandling.Ignore
            }); 
            //.Dump();
Dirk Vollmar
  • 172,527
  • 53
  • 255
  • 316
Maslow
  • 18,464
  • 20
  • 106
  • 193
3

Similar to @Maslow's solution, you can use another general purpose "ignorer":

var jsonResolver = new IgnorableSerializerContractResolver();
// ignore your specific property
jsonResolver.Ignore(typeof(Foo), "string2");
// ignore single datatype
jsonResolver.Ignore(typeof(System.Data.Objects.DataClasses.EntityObject));
var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };
Community
  • 1
  • 1
drzaus
  • 24,171
  • 16
  • 142
  • 201
  • 5
    That is for ignores during serialization, this question is about deserialization. – Maestro Sep 17 '14 at 10:11
  • @Alrehamy what? To quote the question > "...or fully ignore it during deserialization" – drzaus Jan 05 '17 at 18:43
  • 1
    Years later based on [@ohad-bitton's answer](https://stackoverflow.com/a/51011104/1037948) I think the resolver _doesn't_ work both ways. Oh well. – drzaus Jul 10 '18 at 21:14
  • I ran into the same thing, but was able to fix it by overriding the `CreateProperties` method: ` protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { var properties = base.CreateProperties(type, memberSerialization); foreach (var p in Ignores.SelectMany((type) => type.Value)) { properties.Add(new JsonProperty() { PropertyName = p }); } return properties; }` – daedalus12 Sep 08 '20 at 20:51
3

this code worked like a charm to me:

using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
public class PropertyRenameAndIgnoreSerializerContractResolver : DefaultContractResolver
{
    private readonly Dictionary<Type, HashSet<string>> _ignores;
    private readonly Dictionary<Type, Dictionary<string, string>> _renames;

    public PropertyRenameAndIgnoreSerializerContractResolver()
    {
        _ignores = new Dictionary<Type, HashSet<string>>();
        _renames = new Dictionary<Type, Dictionary<string, string>>();
    }

    public void IgnoreProperty(Type type, params string[] jsonPropertyNames)
    {
        if (!_ignores.ContainsKey(type))
            _ignores[type] = new HashSet<string>();

        foreach (var prop in jsonPropertyNames)
            _ignores[type].Add(prop);
    }

    public void RenameProperty(Type type, string propertyName, string newJsonPropertyName)
    {
        if (!_renames.ContainsKey(type))
            _renames[type] = new Dictionary<string, string>();

        _renames[type][propertyName] = newJsonPropertyName;
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        if (IsIgnored(property.DeclaringType, property.PropertyName))
        {
            property.ShouldSerialize = i => false;
            property.Ignored = true;
        }

        if (IsRenamed(property.DeclaringType, property.PropertyName, out var newJsonPropertyName))
            property.PropertyName = newJsonPropertyName;

        return property;
    }

    private bool IsIgnored(Type type, string jsonPropertyName)
    {
        if (!_ignores.ContainsKey(type))
            return false;

        return _ignores[type].Contains(jsonPropertyName);
    }

    private bool IsRenamed(Type type, string jsonPropertyName, out string newJsonPropertyName)
    {
        Dictionary<string, string> renames;

        if (!_renames.TryGetValue(type, out renames) || !renames.TryGetValue(jsonPropertyName, out newJsonPropertyName))
        {
            newJsonPropertyName = null;
            return false;
        }

        return true;
    }
}

//ignore number2 in Foo Example

public class Foo
{
public string number1 { get; set; }
public string number2 { get; set; }
public string number3 { get; set; }
}
    string Foojson = @"[
    {
        ""number1"": 1,
        ""number2"": 12345678901234567890,
        ""number3"": 3
    },
    {
        ""number1"": 9,
        ""number2"": 12345678901234567890,
        ""number3"": 8
    }
]";
var jsonResolverFoo = new PropertyRenameAndIgnoreSerializerContractResolver();
jsonResolverFoo.IgnoreProperty(typeof(Foo), "number2");
var serializerSettingsFoo = new JsonSerializerSettings();
serializerSettingsFoo.ContractResolver = jsonResolverFoo;
var deserializedJsonFoo = JsonConvert.DeserializeObject<List<Foo>>(Foojson, serializerSettingsFoo);

/* Resource Link: https://blog.rsuter.com/advanced-newtonsoft-json-dynamically-rename-or-ignore-properties-without-changing-the-serialized-class/ */

MexMercury
  • 121
  • 1
  • 5
1

Adding to drzaus answer: You can use the DefaultContractResolver he suggested .. just in its CreateProperty use property.Ignored = true; instead of property.ShouldSerialize, then its good either when you pass the JsonSerializerSettings to the DeserializeObject function or the SerializeObject function.

Ohad Bitton
  • 460
  • 2
  • 14
  • Doesn't work for me; I also tried `GetIsSpecified`, and then [tried to look at JsonConvert's source code](https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/JsonSerializer.cs) and I don't actually think it's possible. Maybe substitute the `property.ValueProvider` with some sort of noop? – drzaus Jul 10 '18 at 21:13
  • i tested it and it did work.. what happened in your case? – Ohad Bitton Aug 07 '18 at 15:00
  • 1
    Nothing happened in my case. It ignored my ignoring of the property and continued to deserialize. `¯\_(ツ)_/¯` – drzaus Aug 10 '18 at 21:42
1

Alternative;

If ResponseAttribute has in model or string parameters

public class ResponseAttribute : Attribute { }

public class ModelItem
{
    [Response]
    public Guid Id { get; set; }
}

Code;

public class CustomJsonSerializer : JsonSerializerSettings
{
    public CustomJsonSerializer()
    {
        ContractResolver = new CustomContractResolver();
    }

    public CustomJsonSerializer(params string[] members)
    {
        ContractResolver = new CustomContractResolver(members);
    }

    public class CustomContractResolver : DefaultContractResolver
    {
        public string[] Members { get; set; }
        public CustomContractResolver(params string[] _members)
        {
            Members = _members;
        }

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

            if (Members?.Length > 0)
                property.ShouldSerialize = instance => { return Members.Contains(member.Name); };
            else
                property.ShouldSerialize = instance => { return member.GetCustomAttribute<ResponseAttribute>() != null; };

            return property;
        }
    }
}

Use;

return new JsonResult(model, new CustomJsonSerializer());

or

return new JsonResult(model, new CustomJsonSerializer("Id","Test","Test2"));
dcansyn
  • 78
  • 6
0

I ran into something similar but my class contained List<> and Dictionary<> which were populated and should not be overwritten by whatever the JSON file had. It was easier for me to load the data into a scratch object and then just pull the items I needed as opposed to any other method I could find at the time.

So for this example, something like this...

public class Foo
{
    public string string1 { get; set; }
    public string string2 { get; set; }
    public string string3 { get; set; }
}
List<Foo> foos = new List<Foo>();

List<Foo> tmp= JsonConvert.DeserializeObject<List<Foo>>(json);

foreach(Foo item in tmp)
{
    foos.string1 = tmp.string1;
    foos.string3 = tmp.string3;
}
John T
  • 118
  • 7
0

According to https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to?pivots=dotnet-6-0

Any JSON properties that aren't represented in your class are ignored.