问题
Hopefully someone can help me with the following inconsistency occurring in a large JSON file that I am attempting to deserialize using Newtonsoft.Json.
One of the properties of the object occasionally appears as:
"roles": [
{
"field1" : "value",
"field2" : "value"
}
]
While other times that same property appears as:
"roles": {
"roles": [
{
"field1" : "value",
"field2" : "value"
}
]
}
For reference, this property is implemented in its class as:
[JsonProperty("roles")]
public List<Role> Roles { get; set; }
What I need to happen is that whenever the second situation above occurs, the object contents are deserialized like the first situation. i.e. the "outer" object is discarded/ignored
I have managed to handle another inconsistency in this file when a separate property sometimes occurs as an object and sometimes as an array using the following approach in its class definition:
[JsonConverter(typeof(SingleValueArrayConverter<Address>))]
public List<Address> Location { get; set; }
And implemented as:
public class SingleValueArrayConverter<T> : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object retVal = new Object();
if (reader.TokenType == JsonToken.StartObject)
{
T instance = (T)serializer.Deserialize(reader, typeof(T));
retVal = new List<T>() { instance };
}
else if (reader.TokenType == JsonToken.StartArray)
{
retVal = serializer.Deserialize(reader, objectType);
}
return retVal;
}
public override bool CanConvert(Type objectType)
{
return true;
}
}
However, I am unable to work out this issue. Can anyone help?
回答1:
You can handle this inconsistency with a JsonConverter
also. It will be a little different than the one you have, but the idea is very similar:
public class ArrayOrWrappedArrayConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(List<T>).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return CreateListFromJArray((JArray)token, serializer);
}
if (token.Type == JTokenType.Object)
{
JObject wrapper = (JObject)token;
JProperty prop = wrapper.Properties().FirstOrDefault();
if (prop.Value.Type == JTokenType.Array)
{
return CreateListFromJArray((JArray)prop.Value, serializer);
}
}
// If the JSON is not what we expect, just return an empty list.
// (Could return null or throw an exception here instead if desired.)
return new List<T>();
}
private List<T> CreateListFromJArray(JArray array, JsonSerializer serializer)
{
List<T> list = new List<T>();
serializer.Populate(array.CreateReader(), list);
return list;
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then just add the converter to your Roles
property and you should be good to go:
[JsonProperty("roles")]
[JsonConverter(typeof(ArrayOrWrappedArrayConverter<Role>))]
public List<Role> Roles { get; set; }
Working demo: https://dotnetfiddle.net/F6qgQB
回答2:
the easiest way (not necessarily the cleanest) would be to manually alter the string before deserialising -
jsonString = jsonString.replace("\"roles\": {", "\"rolesContainer\": {");
jsonString = jsonString.replace("\"roles\":{", "\"rolesContainer\": {");
and then in your main code you would have both rolesContainer and roles as fields - and then merge them after
public List<Role> roles { get; set; }
public RoleContainer rolesContainer { get; set; }
public class RoleContainer {
Public List<Role> roles;
}
it's dirty, but it should work
来源:https://stackoverflow.com/questions/63045851/deserialize-inconsistent-json-property