-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature Request] MissingMemberHandling attribute #2975
Comments
Did you perhaps mistakenly confuse A Therefore, Also, STJ's [JsonUnmappedMemberHandling] attribute applies to types, not fields/properties. I fail to see how you would relate its functionality to the [JsonProperty] attribute which applies to properties/fields and not types. All that said, please note that i am not the author/maintainer of Newtonsoft.Json and not associated with the project in any form (i am just a -mostly former- user of Newtonsoft.Json. If you check the commit history as well as the activity of the author of this library here in the Github repo, you will notice that the library basically is legacy and in what i would call "maintenance mode". So, even if your feature suggestions would make sense, it would be extremely unlikely that it would become reality due to the lack of substantial development activities regarding this library. In my opinion, if you are able it's better to switch to STJ. (Note that STJ is also available as a nuget package for .NET versions as old as .NET Framwork 4.6.2.) |
|
To follow up on this: I think the request is for something like: public class AnObject
{
public int ID { get; set; }
public string Name { get; set; }
}
public class AnObjectWithErrors
{
[JsonMissingMember(MissingMemberHandling.Ignore)]
public AnObject Object1 { get; set; }
[JsonMissingMember()]
public AnObject Object2 { get; set; }
} Such that if the json for Object2 includes extra members, regardless of the setting of I think the idea would be to override both the I'm not entirely sure just how useful this would be, or under what circumstances you would want to use it, but it can be done. using Newtonsoft.Json;
public class MissingMemberConverter : JsonConverter
{
private readonly MissingMemberHandling missingMemberHandling;
private readonly JsonConverter? baseConverter;
public MissingMemberConverter() : this(MissingMemberHandling.Error) { }
public MissingMemberConverter(MissingMemberHandling missingMemberHandling) => this.missingMemberHandling = missingMemberHandling;
public MissingMemberConverter(Type baseConverter, params object[]? baseConverterArgs) :
this(MissingMemberHandling.Error, baseConverter, baseConverterArgs) { }
public MissingMemberConverter(MissingMemberHandling missingMemberHandling, Type baseConverter, params object[]? baseConverterArgs) :
this(missingMemberHandling) =>
this.baseConverter = (JsonConverter?)Activator.CreateInstance(baseConverter, baseConverterArgs);
public override bool CanConvert(Type objectType) => baseConverter is null || baseConverter.CanConvert(objectType);
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
JsonConverter? converter = baseConverter?.CanRead ?? false ? baseConverter : null;
if (baseConverter is null) {
var contract = serializer.ContractResolver.ResolveContract(objectType);
converter = contract.Converter ?? serializer.GetMatchingConverter(contract.UnderlyingType) as JsonConverter ?? contract.InternalConverter;
}
var previousHandling = serializer.MissingMemberHandling;
serializer.MissingMemberHandling = missingMemberHandling;
try
{
var value = converter is null ?
serializer.Deserialize(reader, objectType) :
converter.ReadJson(reader, objectType, existingValue, serializer);
return value;
}
catch (Newtonsoft.Json.JsonSerializationException ex)
{
serializer.TraceWriter?.Trace(System.Diagnostics.TraceLevel.Error, "Serialization Exception in MissingMemberConverter", ex);
throw;
}
finally
{
serializer.MissingMemberHandling = previousHandling;
}
}
public override bool CanRead => true;
public override bool CanWrite => baseConverter?.CanWrite ?? false;
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
if (baseConverter is null) throw new NotImplementedException();
baseConverter.WriteJson(writer, value, serializer);
}
}
static class JsonSerializerExtensions
{
public static JsonConverter? GetMatchingConverter(this JsonSerializer serializer, Type objectType)
{
for (int i = 0; i < serializer.Converters.Count; i++)
{
JsonConverter converter = serializer.Converters[i];
if (converter.CanConvert(objectType))
{
return converter;
}
}
return null;
}
} Use as follows: using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public static class Program
{
public static void Main()
{
var aowe = new AnObjectWithErrors
{
Object1 = new AnObject
{
ID = 1,
Name = "a",
},
Object2 = new AnObject
{
ID = 2,
Name = "b",
},
MissingMemberHandling = MissingMemberHandling.Error
};
Console.WriteLine(JsonConvert.SerializeObject(aowe)); // yields {"Object1":{"ID":1,"Name":"a"},"Object2":{"ID":2,"Name":"b"}}
aowe = TryDeser("{\"Object1\":{\"ID\":1,\"Name\":\"a\"},\"Object2\":{\"ID\":2,\"Name\":\"b\"},\"MissingMemberHandling\":\"error\"}", "Good");
aowe = TryDeser("{\"Object1\":{\"ID\":1,\"Name\":\"a\",\"foo\":\"bar\"},\"Object2\":{\"ID\":2,\"Name\":\"b\"},\"MissingMemberHandling\":\"ignore\"}", "Object1");
aowe = TryDeser("{\"Object1\":{\"ID\":1,\"Name\":\"a\",\"foo\":\"bar\"},\"Object2\":{\"ID\":2,\"Name\":\"b\"},\"MissingMemberHandling\":\"ignore\"}", "Object1", MissingMemberHandling.Error);
aowe = TryDeser("{\"Object1\":{\"ID\":1,\"Name\":\"a\"},\"Object2\":{\"ID\":2,\"Name\":\"b\",\"foo\":\"bar\"},\"MissingMemberHandling\":\"ignore\"}", "Object2");
aowe = TryDeser("{\"Object1\":{\"ID\":1,\"Name\":\"a\",\"foo\":\"bar\"},\"Object2\":{\"ID\":2,\"Name\":\"b\",\"foo\":\"bar\"},\"MissingMemberHandling\":\"ignore\"}", "Both");
}
private static AnObjectWithErrors? TryDeser(string value, string description, MissingMemberHandling missingMemberHandling = MissingMemberHandling.Ignore)
{
try
{
Console.WriteLine($"Deserializing {description}");
Console.WriteLine(JsonConvert.SerializeObject(JsonConvert.DeserializeObject(value), Formatting.Indented));
var ser = new JsonSerializerSettings { MissingMemberHandling = missingMemberHandling };
var aowe = JsonConvert.DeserializeObject<AnObjectWithErrors>(value, ser);
Console.WriteLine($"{description} Succeeded");
}
catch (Exception ex)
{
Console.WriteLine($"{description} Failed {ex.Message}");
}
return null;
}
}
public class AnObject
{
public int ID { get; set; }
public string Name { get; set; }
}
public class AnObjectWithErrors
{
[JsonConverter(typeof(MissingMemberConverter), MissingMemberHandling.Ignore)] // Note this applies to this property and all nested objects
public AnObject Object1 { get; set; }
[JsonConverter(typeof(MissingMemberConverter))] // Note this applies to this property and all nested objects
public AnObject Object2 { get; set; }
// This doesn't really make sense - but shows how a nested converter may be specified
[JsonConverter(typeof(MissingMemberConverter), typeof(StringEnumConverter), new object[] { typeof(CamelCaseNamingStrategy) })]
public MissingMemberHandling MissingMemberHandling { get; set; }
} Note that this will still fail on [JsonObject(MissingMemberHandling = MissingMemberHandling.Error)]
public class AnObject
{
public int ID { get; set; }
public string Name { get; set; }
} I think there is probably a way round that - but since this was for proof of concept - I leave that as an exercise for the reader. Unfortunately [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class JsonMissingMemberAttribute : JsonConverterAttribute
{
public JsonMissingMemberAttribute(MissingMemberHandling missingMemberHandling = MissingMemberHandling.Error) :
base(typeof(MissingMemberConverter), missingMemberHandling)
{ }
public JsonMissingMemberAttribute(Type baseConverter, params object[]? baseParams) : base(typeof(MissingMemberConverter), baseConverter, baseParams)
{ }
public JsonMissingMemberAttribute(MissingMemberHandling missingMemberHandling, Type baseConverter, params object[]? baseParams) :
base(missingMemberHandling, typeof(MissingMemberConverter), baseConverter, baseParams)
{ }
} |
@CZEMacLeod this example:
is exactly what I have in mind for usage of MissingMemberHandling attribute. I find it useful in cases when you generally don't want to check for missing members but after a change in a property name (and type possibly), you want to make sure that old name is not used by mistake. I think your implementation of As for comparison of this library to System.Text.Json, I can see that this repo is for long period now practically inactive, but still in most cases I find Newtonsoft to be more reasonable and easier to work with. For example serializing double |
It would be useful to expose
MissingMemberHandling
serialization setting as aJsonPropertyAttribute
, in order to override it for specific members only.System.Text.Json does it with JsonUnmappedMemberHandling attribute.
The text was updated successfully, but these errors were encountered: