I've been having trouble finding a question about my particular case:
I need to deserialize the following JSON:
{
"name": "My Farm",
"barns": [
{
"name"": "Barn A"",
"animalTypes": [
"Cow",
"Goat"
]
}
]
}
to the following code model:
public class Farm
{
public string name;
public Barn[] barns;
}
public class Barn
{
public string name;
public AnimalType[] animalTypes;
}
public class AnimalType
{
public int typeID;
}
The problem: I need to deserialize a JSON string (which described an animal species name) into an 'AnimalType' object which contains a "type ID" int. This requirement is not something I can change.
To get the animal's integer type ID, I have access to an externally supplied "AnimalTypeResolver" class, which looks like this:
public class AnimalTypeResolver
{
public int GetAnimalType(string animalTypeName)
{
// queries a map to return the right ID.
}
}
I can query that resolver to get the int value I need to store for each animal type.
So, I tried to write a custom JSONConverter for AnimalType:
public class AnimalTypeConverter : JsonConverter<AnimalType>
{
public AnimalTypeResolver animalTypeResolver;
public AnimalTypeConverter(AnimalTypeResolver animalTypeResolver)
{
this.animalTypeResolver = animalTypeResolver;
}
public override bool CanWrite => false;
public override AnimalType ReadJson(ref JsonReader reader, Type objectType, AnimalType existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
string animalTypeName = (string)reader.Value;
return new AnimalType
{
typeID = animalTypeResolver.GetAnimalType(animalTypeName)
};
}
return null;
}
public override void WriteJson(JsonWriter writer, AnimalType value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And my deserialization code looks like this:
string farmJSON =
@"{
""name"": ""My Farm"",
""barns"": [
{
""name"": ""Barn A"",
""animalTypes"": [
""Cow"",
""Goat""
]
}
]
}";
JsonSerializer serializer = new JsonSerializer();
serializer.Converters.Add(new AnimalTypeConverter(animalTypeResolver)); // this animalTypeResolver is supplied from elsewhere in code.
Farm farm = JsonConvert.DeserializeObject<Farm>(farmJSON);
But I get a runtime error:
ArgumentException: Could not cast or convert from System.String to AnimalType.
From reading similar questions, I believe my problem is that I'm trying to deserialize a nested field of a custom type (AnimalType[]). Other answers have explained that JToken.FromObject() creates a new JsonSerializer for each level of deserialization, which has no concept of the JsonConverters I added to a higher-level serializer.
However, For one reason or another, the other questions on this site have answers which aren't exactly applicable to my case.
How I can use my custom JsonConverter, to handle a case in which the data is deeply nested?
If anyone can offer advice about how to make this work, thank you!
JsonConvert.DeserializeObject<Farm>(farmJSON)
does not use your JsonSerializer instance. I mean, how could it possibly access the serializer variable that you created and assigned in your code?You have two choices: Either use the Deserialize method of the serializer instance you did just setup to deserialize your json data and not use JsonConvert.DeserializeObject .
Or instead of setting up a serializer, define some JsonSerializerSettings with your custom JsonConverter and pass those settings to the JsonConvert.DeserializeObject method. Alternatively you could instead also define default serialization settings for JsonConvert.DeserializeObject as demonstrated here in the Newtonsoft.Json documentation: https://www.newtonsoft.com/json/help/html/DefaultSettings.htm