简体   繁体   中英

JSON.Net - How to deserialize a nested field of a custom type

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM