1

Just wondering if anyone knows of the best way to upgrade a JSON structure Deserialisation to a new class type.

To further explain the legacy value was

        public string author;

this has now been updated in the api to the following

 public class Author
{
    public string name;
    public string email;
    public string url;
}

public Author author;

So now I have an issue where any legacy data does not deserialize into this correctly as it used to be a string and now its a class.

My current solution is if it fails to deserialize then to do it into a class that had the old structure and then use this to go into the new one, but i feel there must be a better way to cast the old sting value into the new class value as part of the process.

Thanks

EDIT-1:

Ok i have made some headway with a converter below

public class AuthorConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Author user = (Author)value;
        writer.WriteValue(user);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        Author author = new Author();

        Debug.Log(objectType + "  " + reader.Value);
        if (reader.TokenType == JsonToken.String)
        {
            author.name = (string) reader.Value;
        }
        else if(reader.TokenType == JsonToken.StartObject)
        {
            try
            {
                JObject jObject = JObject.Load(reader);

                if (jObject.TryGetValue("name", out JToken name))
                {
                    author.name = name.Value<string>();
                }
                if (jObject.TryGetValue("email", out JToken email))
                {
                    author.email = email.Value<string>();
                }

                if (jObject.TryGetValue("url", out JToken url))
                {
                    author.url = url.Value<string>();
                }

            }
            catch (Exception e)
            {
                UnityEngine.Debug.Log(e);
                throw;
            }

        }
        return author;
    }

Appears to all be working, but feels a bit fiddly to have to get the values 1 by 1 and convert over, i tried using the jObject.ToObject method but appeared to cause an infinite loop. Either way its working, but im sure there is a better way, so still open for ideas.

1 Answers1

0

Since your Author has a default (parameterless) constructor, you can avoid having to populate each property manually by using JsonSerializer.Populate(JsonReader, Object). And to avoid having to write your own WriteJson() method, override CanWrite and return false.

Thus your converter can be rewritten as follows:

public class AuthorConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) { return objectType == typeof(Author); }

    public override bool CanWrite { get { return false; } }

    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)
    {
        try
        {
            if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
                return null; // Or throw an exception if you don't want to allow null.
            else if (reader.TokenType == JsonToken.String)
                return new Author { name = (string) reader.Value };
            else
            {
                var author = new Author();
                serializer.Populate(reader, author);
                return author;
            }           
        }
        catch (Exception ex)
        {
            UnityEngine.Debug.Log(ex);
            throw;
        }
    }
}

public static partial class JsonExtensions
{
    // Skip past any comments to the next content token, asserting that the file was not truncated.
    public static JsonReader MoveToContentAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (reader.TokenType == JsonToken.None)       // Skip past beginning of stream.
            reader.ReadAndAssert();
        while (reader.TokenType == JsonToken.Comment) // Skip past comments.
            reader.ReadAndAssert();
        return reader;
    }

    // Read (advance to) the next token, asserting that the file was not truncated.
    public static JsonReader ReadAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (!reader.Read())
            throw new JsonReaderException("Unexpected end of JSON stream.");
        return reader;
    }
}

Notes:

Demo fiddle here.

dbc
  • 104,963
  • 20
  • 228
  • 340