private/runtime/Nodes/JsonNode.cs

using System;
using System.IO;
using System.Text;

namespace Carbon.Json
{
    using Parser;

    public abstract class JsonNode
    {
        public abstract JsonType Type { get; }

        public virtual JsonNode this[int index] => throw new NotImplementedException();

        public virtual JsonNode this[string name]
        {
            get => throw new NotImplementedException();
            set => throw new NotImplementedException();
        }

        #region Type Helpers
        
        public bool IsArray => Type == JsonType.Array;

        public bool IsDate => Type == JsonType.Date;

        public bool IsObject => Type == JsonType.Object;

        public bool IsNumber => Type == JsonType.Number;

        public bool IsNull => Type == JsonType.Null;

        #endregion

        public void WriteTo(TextWriter textWriter, bool pretty = true)
        {
            var writer = new JsonWriter(textWriter, pretty);

            writer.WriteNode(this);
        }

        public T As<T>()
            where T : new()
            => new JsonSerializer().Deseralize<T>((JsonObject)this);

        public T[] ToArrayOf<T>()
        {
            return (T[])new JsonSerializer().DeserializeArray(typeof(T[]), (JsonArray)this);
        }

        #region ToString Overrides

        public override string ToString() => ToString(pretty: true);

        public string ToString(bool pretty)
        {
            var sb = new StringBuilder();

            using (var writer = new StringWriter(sb))
            {
                WriteTo(writer, pretty);

                return sb.ToString();
            }
        }

        #endregion

        #region Static Constructors

        public static JsonNode Parse(string text)
        {
            return Parse(new SourceReader(new StringReader(text)));
        }

        public static JsonNode Parse(TextReader textReader)
            => Parse(new SourceReader(textReader));

        private static JsonNode Parse(SourceReader sourceReader)
        {
            using (var parser = new JsonParser(sourceReader))
            {
                return parser.ReadNode();
            }
        }

        public static JsonNode FromObject(object instance)
            => new JsonSerializer().Serialize(instance);

        #endregion

        #region Implict Casts

        public static implicit operator string(JsonNode node) => node.ToString();

        #endregion

        #region Explict Casts

        public static explicit operator DateTime(JsonNode node)
        {
            switch (node.Type)
            {
                case JsonType.Date:
                    return ((JsonDate)node).ToDateTime();

                case JsonType.String:
                    return JsonDate.Parse(node.ToString()).ToDateTime();

                case JsonType.Number:
                    var num = (JsonNumber)node;

                    if (num.IsInteger)
                    {
                        return DateTimeOffset.FromUnixTimeSeconds(num).UtcDateTime;
                    }
                    else
                    {
                        return DateTimeOffset.FromUnixTimeMilliseconds((long)((double)num * 1000)).UtcDateTime;
                    }
            }

            throw new ConversionException(node, typeof(DateTime));
        }

        public static explicit operator DateTimeOffset(JsonNode node)
        {
            switch (node.Type)
            {
                case JsonType.Date : return ((JsonDate)node).ToDateTimeOffset();
                case JsonType.String : return JsonDate.Parse(node.ToString()).ToDateTimeOffset();

                case JsonType.Number:
                    var num = (JsonNumber)node;

                    if (num.IsInteger)
                    {
                        return DateTimeOffset.FromUnixTimeSeconds(num);
                    }
                    else
                    {
                        return DateTimeOffset.FromUnixTimeMilliseconds((long)((double)num * 1000));
                    }

            }

            throw new ConversionException(node, typeof(DateTimeOffset));
        }

        public static explicit operator float(JsonNode node)
        {
            switch (node.Type)
            {
                case JsonType.Number : return (JsonNumber)node;
                case JsonType.String : return float.Parse(node.ToString());
            }

            throw new ConversionException(node, typeof(float));
        }

        public static explicit operator double(JsonNode node)
        {
            switch (node.Type)
            {
                case JsonType.Number : return (JsonNumber)node;
                case JsonType.String : return double.Parse(node.ToString());
            }

            throw new ConversionException(node, typeof(double));
        }

        public static explicit operator decimal(JsonNode node)
        {
            switch (node.Type)
            {
                case JsonType.Number: return (JsonNumber)node;
                case JsonType.String: return decimal.Parse(node.ToString());
            }
            
            throw new ConversionException(node, typeof(decimal));
        }

        public static explicit operator Guid(JsonNode node)
            => new Guid(node.ToString());

        public static explicit operator short(JsonNode node)
        {
            switch (node.Type)
            {
                case JsonType.Number : return (JsonNumber)node;
                case JsonType.String : return short.Parse(node.ToString());
            }

            throw new ConversionException(node, typeof(short));
        }

        public static explicit operator int(JsonNode node)
        {
            switch (node.Type)
            {
                case JsonType.Number : return (JsonNumber)node;
                case JsonType.String : return int.Parse(node.ToString());
            }

            throw new ConversionException(node, typeof(int));
        }

        public static explicit operator long(JsonNode node)
        {
            switch (node.Type)
            {
                case JsonType.Number: return (JsonNumber)node;
                case JsonType.String: return long.Parse(node.ToString());
            }

            throw new ConversionException(node, typeof(long));
        }

        public static explicit operator bool(JsonNode node)
           => ((JsonBoolean)node).Value;

        public static explicit operator ushort(JsonNode node)
            => (JsonNumber)node;

        public static explicit operator uint(JsonNode node)
            => (JsonNumber)node;

        public static explicit operator ulong(JsonNode node)
            => (JsonNumber)node;

        public static explicit operator TimeSpan(JsonNode node)
            => TimeSpan.Parse(node.ToString());

        public static explicit operator Uri(JsonNode node)
        {
            if (node.Type == JsonType.String)
            {
                return new Uri(node.ToString());
            }

            throw new ConversionException(node, typeof(Uri));
        }

        #endregion
    }
}