1

Here is a sample application that shows custom serialization using a JsonConverter for a List. I register a custom JsonSerializerSettings with JsonConvert. These settings have my ListOfGuidConverter installed.

This works beautifully when you are serializing and deserializing a List as the root object of serialization. As soon as you have a different root object and the List is a property, JSON.Net throws exceptions saying unexpected end.

What can I do to fix this?

Thanks Rich

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using PB8.Peep;

namespace JsonTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var ListOfGuidsConverter = new ListJsonConverter<Guid>();
                var jss = new JsonSerializerSettings();
                jss.Converters.Add(new ListofGuidJsonConverter());

                string value1 = JsonConvert.SerializeObject(GetSampleList());
                Console.WriteLine(value1);

                JsonConvert.DefaultSettings = () => jss;

                string value2 = JsonConvert.SerializeObject(GetSampleList());
                Console.WriteLine();
                Console.WriteLine(value2);

                var value3 = JsonConvert.DeserializeObject<List<Guid>>(value2);

                string value4 = JsonConvert.SerializeObject(value3);

                Console.WriteLine();
                Console.WriteLine(value4);

                var value5 = JsonConvert.SerializeObject(GetSample());
                Console.WriteLine();
                Console.WriteLine(value5);

                var value6 = JsonConvert.DeserializeObject<Sample>(value5);

                var value7 = JsonConvert.SerializeObject(value6);

                Console.WriteLine();
                Console.WriteLine(value7);

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            finally
            {
                Console.ReadLine();
            }
        }

        public static List<Guid> GetSampleList()
        {
            return new List<Guid>
            {
                Guid.NewGuid(),
                Guid.NewGuid(),
                Guid.NewGuid()
            };
        }

        public static Sample GetSample()
        {
            Sample s = new Sample();
            s.List1.Add(Guid.NewGuid());
            s.List1.Add(Guid.NewGuid());
            s.List1.Add(Guid.NewGuid());
            s.List1.Add(Guid.NewGuid());
            s.List1.Add(Guid.NewGuid());

            return s;
        }

        public class Sample
        {
            public Sample()
            {
                List1 = GetSampleList();
            }

            public List<Guid> List1 { get; set; }
        }
    }




    public class ListofGuidJsonConverter : JsonConverter
    {
        private const string CapacityPropertyName = "c";
        private const string ListPropertyName = "l";
        private static readonly Type _type = typeof(List<Guid>);

        public override bool CanConvert(Type objectType)
        {
            bool canConvert = objectType == _type;
            return canConvert;
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            const int DefaultCapacity = 4;

            if (reader.TokenType == JsonToken.StartObject)
                reader.Read();

            if (reader.TokenType == JsonToken.PropertyName)
                reader.Read();

            int capacity = DefaultCapacity;
            if (reader.TokenType == JsonToken.Integer)
            {
                capacity = Convert.ToInt32(reader.Value);
                reader.Read();
            }

            var listOfT = new List<Guid>(capacity);

            if (reader.TokenType == JsonToken.PropertyName)
                reader.Read();

            if (reader.TokenType == JsonToken.StartArray)
                reader.Read();

            for (int i = 0; i < capacity; i++)
            {
                var item = new Guid(Convert.ToString(reader.Value));
                listOfT.Add(item);
                reader.Read();
            }

            if (reader.TokenType == JsonToken.EndArray)
                reader.Read();

            if (reader.TokenType == JsonToken.EndObject)
                reader.Read();

            return listOfT;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var listOfT = (List<Guid>)value;

            writer.WriteStartObject();

            writer.WritePropertyName(CapacityPropertyName);
            writer.WriteValue(listOfT.Count);

            writer.WritePropertyName(ListPropertyName);
            writer.WriteStartArray();

            foreach (var item in listOfT)
                writer.WriteValue(item);

            writer.WriteEndArray();

            writer.WriteEndObject();
        }
    }
}

1 Answer 1

1

It is very easy to get the reader logic wrong when implementing a JsonConverter. Most likely you are reading one too many times somewhere along the line, which is throwing off the serializer later on. Rather than trying to figure out where it's going off the tracks, I would recommend taking a different approach: use the higher-level LINQ-to-JSON API (JObjects) inside your converter instead, and let it deal with the reader. Your code will be much shorter and more understandable / maintainable for it.

Here is an alternate implementation of your converter using LINQ-to-JSON. It passes all of your tests.

public class ListofGuidJsonConverter : JsonConverter
{
    private const string CapacityPropertyName = "c";
    private const string ListPropertyName = "l";

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(List<Guid>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject obj = JObject.Load(reader);
        int capacity = obj[CapacityPropertyName].Value<int>();
        List<Guid> list = new List<Guid>(capacity);
        foreach (JToken token in obj[ListPropertyName].Children())
        {
            list.Add(new Guid(token.ToString()));
        }
        return list;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        List<Guid> list = (List<Guid>)value;
        JObject obj = new JObject();
        obj.Add(CapacityPropertyName, list.Count);
        JArray array = new JArray();
        foreach (Guid guid in list)
        {
            array.Add(guid.ToString());
        }
        obj.Add(ListPropertyName, array);
        obj.WriteTo(writer);
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

AWESOME! This seems to have fixed it. I am usually not a fan of LINQ but I really like the implementation here. Thanks!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.