[Fork] Json.NET startup fiddle.

CSharp · Console

/Program.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters;
using System.IO;
using System.Data;
using System.ComponentModel.DataAnnotations;
using System.Globalization;


using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;	

    internal class WhatToMineCalculatorsResponse
    {
        // Should be Dictionary???
        [JsonProperty("coins")]
        public IList<WhatToMineCalculatorResponse> Coins { get; set; }
    }

    internal class WhatToMineCalculatorResponse
    {
        readonly string _name;

        public WhatToMineCalculatorResponse(string name)
        {
            this._name = name;
        }

        // I want the key set in this field
        public string Name { get { return _name; } }

        [JsonProperty("id")]
        public int Id { get; set; }

        [JsonProperty("tag")]
        public string Symbol { get; set; }

        [JsonProperty("status")]
        public string Status { get; set; }

        [JsonProperty("algorithm")]
        public string Algo { get; set; }

        [JsonProperty("listed")]
        public bool IsListed { get; set; }
    }

    internal class WhatToMineCalculatorResponseListConverter : KeyedListToJsonObjectConverterBase<WhatToMineCalculatorResponse>
    {
        protected override string KeyPropertyUnderlyingName // => nameof(WhatToMineCalculatorResponse.Name);
        {
            get { return "Name"; }
        }
    }

    public abstract class KeyedListToJsonObjectConverterBase<T> : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            if (objectType.IsArray)
                return false;
            return typeof(IList<T>).IsAssignableFrom(objectType);
        }

        protected abstract string KeyPropertyUnderlyingName { get; }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            // Get the key property name from the underlying name
            var itemContract = serializer.ContractResolver.ResolveContract(typeof(T)) as JsonObjectContract;
            if (itemContract == null)
                throw new JsonSerializationException(string.Format("type {0} is not serialized as a JSON object"));
            var keyProperty = itemContract.Properties.Where(p => p.UnderlyingName == KeyPropertyUnderlyingName).SingleOrDefault();
            if (keyProperty == null)
                throw new JsonSerializationException(string.Format("Key property {0} not found", KeyPropertyUnderlyingName));

            // Validate initial token.
            if (reader.SkipComments().TokenType == JsonToken.Null)
                return null;
            if (reader.TokenType != JsonToken.StartObject)
                throw new JsonSerializationException(string.Format("Unexpected token {0} at {1}", reader.TokenType, reader.Path));

            // Allocate the List<T>.  (It might be some subclass of List<T>, so use the default creator.
            var list = existingValue as ICollection<T> ?? (ICollection<T>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();

            // Process each key/value pair.
            while (reader.Read())
            {
                switch (reader.TokenType)
                {
                    case JsonToken.Comment:
                        break;
                    case JsonToken.EndObject:
                        return list;
                    case JsonToken.PropertyName:
                        {
                            // Get the name.
                            var name = (string)reader.Value;
                            reader.ReadAndAssert();
                            // Load the object
                            var jItem = JObject.Load(reader);
                            // Add the name property
                            jItem.Add(keyProperty.PropertyName, name);
                            // Deserialize the item and add it to the list.
                            list.Add(jItem.ToObject<T>(serializer));
                        }
                        break;
                    default:
                        {
                            throw new JsonSerializationException(string.Format("Unexpected token {0} at {1}", reader.TokenType, reader.Path));
                        }
                }
            }
            // Should not come here.
            throw new JsonSerializationException("Unclosed object at path: " + reader.Path);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            // Get the key property name from the underlying name
            var itemContract = serializer.ContractResolver.ResolveContract(typeof(T)) as JsonObjectContract;
            if (itemContract == null)
                throw new JsonSerializationException(string.Format("type {0} is not serialized as a JSON object"));
            var keyProperty = itemContract.Properties.Where(p => p.UnderlyingName == KeyPropertyUnderlyingName).SingleOrDefault();
            if (keyProperty == null)
                throw new JsonSerializationException(string.Format("Key property {0} not found", KeyPropertyUnderlyingName));

            var converters = serializer.Converters.ToArray();
            var list = (IEnumerable<T>)value;
            writer.WriteStartObject();
            foreach (var item in list)
            {
                var jItem = JObject.FromObject(item, serializer);
                var name = (string)jItem[keyProperty.PropertyName];
                jItem.Remove(keyProperty.PropertyName);
                writer.WritePropertyName(name);
                jItem.WriteTo(writer, converters);
            }
            writer.WriteEndObject();
        }
    }

    public static partial class JsonExtensions
    {
        public static JsonReader SkipComments(this JsonReader reader)
        {
            while (reader.TokenType == JsonToken.Comment && reader.Read())
                ;
            return reader;
        }

        public static void ReadAndAssert(this JsonReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException();
            if (!reader.Read())
            {
                new JsonReaderException(string.Format("Unexpected end at path {0}", reader.Path));
            }
        }
    }

    class TestClass
    {
        public static void Test()
        {
            var responseString = GetJSON();

            var settings = new JsonSerializerSettings
            {
                Converters = { new WhatToMineCalculatorResponseListConverter() },
            };
            var root = JsonConvert.DeserializeObject<WhatToMineCalculatorsResponse>(responseString, settings);

            var json3 = JsonConvert.SerializeObject(root, Formatting.Indented, settings);

            Console.WriteLine("\nDeserialized and re-serialized {0} using WhatToMineCalculatorResponseListConverter when writing:", root);
            Console.WriteLine(json3);
            Assert.IsTrue(JObject.Parse(responseString)["coins"].Children().OfType<JProperty>().Select(p => p.Name)
                .SequenceEqual(JObject.Parse(json3)["coins"].Children().OfType<JProperty>().Select(p => p.Name)));

            var json2 = JsonConvert.SerializeObject(root, Formatting.Indented);

            Console.WriteLine("\nDeserialized and re-serialized {0} without using WhatToMineCalculatorResponseListConverter when writing:", root);
            Console.WriteLine(json2);
        }

        static string GetJSON()
        {
            var json = @"{
    coins: {
        365Coin: {
            id: 74,
            tag: ""365"",
            algorithm: ""Keccak"",
            lagging: true,
            listed: false,
            status: ""No available stats"",
            testing: false
        },
        Aiden: {
            id: 65,
            tag: ""ADN"",
            algorithm: ""Scrypt-OG"",
            lagging: true,
            listed: false,
            status: ""No available stats"",
            testing: false
        },
        Adzcoin: {
            id: 157,
            tag: ""ADZ"",
            algorithm: ""X11"",
            lagging: false,
            listed: false,
            status: ""Active"",
            testing: false
        }
    }
}";
            return json;
        }
    }

	public class Program
	{
		public static void Main()
		{
			Console.WriteLine("Environment version: " + Environment.Version);
			Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
			Console.WriteLine();

			try
			{
				TestClass.Test();
			}
			catch (Exception ex)
			{
				Console.WriteLine("Failed with unhandled exception: ");
				Console.WriteLine(ex);
				throw;
			}
		}
	}

    public class AssertionFailedException : System.Exception
    {
        public AssertionFailedException() : base() { }

        public AssertionFailedException(string s) : base(s) { }
    }

    public static class Assert
    {
        public static void IsTrue(bool value)
        {
            IsTrue(value, "failed");
        }

        public static void IsTrue(bool value, string message)
        {
            if (value == false)
                throw new AssertionFailedException(message ?? "failed");
        }
    }
;