All Downloads are FREE. Search and download functionalities are using the official Maven repository.

runtime.csharp.IRT.Marshaller.JsonNetMarshaller.cs Maven / Gradle / Ivy

The newest version!

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using IRT.Transport;
using Newtonsoft.Json.Linq;

namespace IRT.Marshaller {
    public class JsonNetMarshaller: IJsonMarshaller {
        private JsonSerializerSettings settings;
        private JsonConverter[] webSocketConverters;

        public JsonNetMarshaller(bool pretty = false) {
            settings = new JsonSerializerSettings();
            settings.Converters.Add(new StringEnumConverter());
            settings.NullValueHandling = NullValueHandling.Ignore;
            settings.TypeNameHandling = TypeNameHandling.None;
            settings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
            settings.DateParseHandling = DateParseHandling.None;
            settings.Formatting = pretty ? Formatting.Indented : Formatting.None;
            webSocketConverters = new JsonConverter[] {
                new WebSocketRequestMessage_JsonNetConverter(),
                new WebSocketResponseMessage_JsonNetConverter(),
                new WebSocketFailureMessage_JsonNetConverter(),
                new WebSocketMessageBase_JsonNetConverter()
            };
        }

        public string Marshal(I data) {
            var ti = typeof(I);
            // For void responses, we need to provide something that wouldn't break the marshaller
            if (ti == typeof(IRT.Void)) {
                return "{}";
            }

            if (data is WebSocketRequestMessageJson || data is WebSocketResponseMessageJson ||
                data is WebSocketFailureMessage || data is WebSocketMessageBase) {
                return JsonConvert.SerializeObject(data, webSocketConverters);
            }

            if (ti.IsInterface) {
                if (!(data is IRTTI)) {
                    throw new Exception("Trying to serialize an interface which doesn't expose an IRTTI interface: " + typeof(I).ToString());
                }
                return JsonConvert.SerializeObject(new InterfaceMarshalWorkaround(data as IRTTI), settings);
            } else {
                return JsonConvert.SerializeObject(data, typeof(I), settings);
            }
        }

        public O Unmarshal(string data) {
            var to = typeof(O);
            if (to == typeof(WebSocketRequestMessageJson) || to == typeof(WebSocketResponseMessageJson)) {
                return JsonConvert.DeserializeObject(data, webSocketConverters);
            }
            
            return JsonConvert.DeserializeObject(data, settings);
        }

        [JsonConverter(typeof(InterfaceMarshalWorkaround_JsonNetConverter))]
        private class InterfaceMarshalWorkaround {
            public IRTTI Value;
            public InterfaceMarshalWorkaround(IRTTI value) {
                Value = value;
            }
        }

        private class InterfaceMarshalWorkaround_JsonNetConverter: JsonNetConverter {
            public override void WriteJson(JsonWriter writer, InterfaceMarshalWorkaround holder, JsonSerializer serializer) {
                // Serializing polymorphic type
                writer.WriteStartObject();
                writer.WritePropertyName(holder.Value.GetFullClassName());
                serializer.Serialize(writer, holder.Value);
                writer.WriteEndObject();

            }

            public override InterfaceMarshalWorkaround ReadJson(JsonReader reader, System.Type objectType, InterfaceMarshalWorkaround existingValue, bool hasExistingValue, JsonSerializer serializer) {
                throw new Exception("Should not be used for Reading, workaround only for writing.");
            }
        }
        
        private class WebSocketMessageBase_JsonNetConverter: JsonNetConverter {
            public override void WriteJson(JsonWriter writer, WebSocketMessageBase holder, JsonSerializer serializer) {
                throw new Exception("WebSocketMessageBase should never be serialized.");
            }

            public override WebSocketMessageBase ReadJson(JsonReader reader, System.Type objectType, WebSocketMessageBase existingValue, bool hasExistingValue, JsonSerializer serializer) {
                var json = JObject.Load(reader);
                var kind = json["kind"].Value();
                
                var res = hasExistingValue ? existingValue : new WebSocketMessageBase(kind);
                res.Kind = kind;
                return res;
            }
        }
        
        private class WebSocketFailureMessage_JsonNetConverter: JsonNetConverter {
            public override void WriteJson(JsonWriter writer, WebSocketFailureMessage holder, JsonSerializer serializer) {
                writer.WriteStartObject();
                // Kind
                writer.WritePropertyName("kind");
                writer.WriteValue(holder.Kind);
                
                // Cause
                writer.WritePropertyName("cause");
                writer.WriteValue(holder.Cause);
                
                // Data
                writer.WritePropertyName("data");
                writer.WriteValue(holder.Data);
                
                writer.WriteEndObject();
            }

            public override WebSocketFailureMessage ReadJson(JsonReader reader, System.Type objectType, WebSocketFailureMessage existingValue, bool hasExistingValue, JsonSerializer serializer) {
                var json = JObject.Load(reader);
                
                var kind = json["kind"].Value();
                var res = hasExistingValue ? existingValue : new WebSocketFailureMessage();
                res.Kind = kind;
                res.Cause = json["cause"].Value();
                res.Data = json["data"].Value();
                return res;
            }
        }

        private class WebSocketRequestMessage_JsonNetConverter: JsonNetConverter {
            public override void WriteJson(JsonWriter writer, WebSocketRequestMessageJson holder, JsonSerializer serializer) {
                writer.WriteStartObject();
                // Kind
                writer.WritePropertyName("kind");
                writer.WriteValue(holder.Kind);
                
                // ID
                writer.WritePropertyName("id");
                writer.WriteValue(holder.ID);
                
                // Service
                if (!string.IsNullOrEmpty(holder.Service)) {
                    writer.WritePropertyName("service");
                    writer.WriteValue(holder.Service);
                }
                
                // Method
                if (!string.IsNullOrEmpty(holder.Method)) {
                    writer.WritePropertyName("method");
                    writer.WriteValue(holder.Method);
                }
                
                // Headers
                if (holder.Headers != null && holder.Headers.Count > 0) {
                    writer.WritePropertyName("headers");
                    writer.WriteValue(holder.Headers);
                }
                
                // Data
                if (!string.IsNullOrEmpty(holder.Data)) {
                    writer.WritePropertyName("data");
                    writer.WriteRawValue(holder.Data);
                }
                writer.WriteEndObject();

            }

            public override WebSocketRequestMessageJson ReadJson(JsonReader reader, System.Type objectType, WebSocketRequestMessageJson existingValue, bool hasExistingValue, JsonSerializer serializer) {
                var json = JObject.Load(reader);
                
                var kind = json["kind"].Value();

                var res = hasExistingValue ? existingValue : new WebSocketRequestMessageJson(kind);
                res.Kind = kind;
                res.ID = json["id"].Value();
                res.Service = json["service"] != null ? json["service"].Value() : null;
                res.Method = json["method"] != null ? json["method"].Value() : null;
                res.Headers = json["headers"] != null ? json["headers"].Value>() : null;
                // TODO See ResponseConverter message below, need to change to Reader and use Raw access
                if (json["data"] != null) {
                    var dataObj = json["data"];
                    res.Data = dataObj.ToString();
                }
                return res;
            }
        }
        
        private class WebSocketResponseMessage_JsonNetConverter: JsonNetConverter {
            public override void WriteJson(JsonWriter writer, WebSocketResponseMessageJson holder, JsonSerializer serializer) {
                writer.WriteStartObject();
                // Kind
                writer.WritePropertyName("kind");
                writer.WriteValue(holder.Kind);
                
                // Ref
                writer.WritePropertyName("ref");
                writer.WriteValue(holder.Ref);
                
                // Data
                if (!string.IsNullOrEmpty(holder.Data)) {
                    writer.WritePropertyName("data");
                    writer.WriteRawValue(holder.Data);
                }
                writer.WriteEndObject();
            }

            public override WebSocketResponseMessageJson ReadJson(JsonReader reader, System.Type objectType, WebSocketResponseMessageJson existingValue, bool hasExistingValue, JsonSerializer serializer) {
                var json = JObject.Load(reader);
                var kind = json["kind"].Value();
                
                var res = hasExistingValue ? existingValue : new WebSocketResponseMessageJson(kind);
                res.Kind = kind;
                res.Ref = json["ref"].Value();
                if (json["data"] != null) {
                    var dataObj = json["data"];
                    res.Data = dataObj.ToString();
                }
                /*
                // TODO Avoid unnecessary double parsing and emitting, should use raw reader
                while (reader.Read()) {
                    if (reader.Value != null) {
                        if (reader.TokenType == JsonToken.PropertyName) {
                            Console.WriteLine("Found: " + reader.Value);
                            
                            switch (reader.Value) {
                                case "ref": res.Ref = reader.ReadAsString();
                                    break;
                                case "error": res.Error = reader.ReadAsString();
                                    break;
                                case "data":
                                    var currentDepth = reader.Depth;
                                    while (reader.Read() && reader.Depth > currentDepth) {
                                        reader.
                                    }
                                    var raw = JRaw.Create(reader);;
                                    res.Data = reader.ReadAsBytes().ToString();// raw.ToString();
                                    Console.WriteLine(res.Data);
                                    break;
                            }
                        }
                    }
                }
                */
                return res;
            }
        }
    }

    // From here https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/JsonConverter.cs
    public abstract class JsonNetConverter : JsonConverter {
        public sealed override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
            WriteJson(writer, (T)value, serializer);
        }

        public abstract void WriteJson(JsonWriter writer, T value, JsonSerializer serializer);

        public sealed override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
            return ReadJson(reader, objectType, default(T), false, serializer);
        }

        public abstract T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer);

        public sealed override bool CanConvert(Type objectType) {
            return typeof(T).IsAssignableFrom(objectType);
        }
    }

    public static class JsonNetTimeFormats {
        public static readonly string TslDefault = "yyyy-MM-ddTHH:mm:ss.fff";
        public static readonly string[] Tsl = new string[] {
                    "yyyy-MM-ddTHH:mm:ss",
                    "yyyy-MM-ddTHH:mm:ss.f",
                    "yyyy-MM-ddTHH:mm:ss.ff",
                    "yyyy-MM-ddTHH:mm:ss.fff",
                    "yyyy-MM-ddTHH:mm:ss.ffff",
                    "yyyy-MM-ddTHH:mm:ss.fffff",
                    "yyyy-MM-ddTHH:mm:ss.ffffff",
                    "yyyy-MM-ddTHH:mm:ss.fffffff",
                    "yyyy-MM-ddTHH:mm:ss.ffffffff",
                    "yyyy-MM-ddTHH:mm:ss.fffffffff"
                };

        public static readonly string TszDefault = "yyyy-MM-ddTHH:mm:ss.fffzzz";
        public static readonly string[] Tsz = new string[] {
                   "yyyy-MM-ddTHH:mm:ssZ",
                   "yyyy-MM-ddTHH:mm:ss.fZ",
                   "yyyy-MM-ddTHH:mm:ss.ffZ",
                   "yyyy-MM-ddTHH:mm:ss.fffZ",
                   "yyyy-MM-ddTHH:mm:ss.ffffZ",
                   "yyyy-MM-ddTHH:mm:ss.fffffZ",
                   "yyyy-MM-ddTHH:mm:ss.ffffffZ",
                   "yyyy-MM-ddTHH:mm:ss.fffffffZ",
                   "yyyy-MM-ddTHH:mm:ss.ffffffffZ",
                   "yyyy-MM-ddTHH:mm:ss.fffffffffZ",
                   "yyyy-MM-ddTHH:mm:sszzz",
                   "yyyy-MM-ddTHH:mm:ss.fzzz",
                   "yyyy-MM-ddTHH:mm:ss.ffzzz",
                   "yyyy-MM-ddTHH:mm:ss.fffzzz",
                   "yyyy-MM-ddTHH:mm:ss.ffffzzz",
                   "yyyy-MM-ddTHH:mm:ss.fffffzzz",
                   "yyyy-MM-ddTHH:mm:ss.ffffffzzz",
                   "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
                   "yyyy-MM-ddTHH:mm:ss.ffffffffzzz",
                   "yyyy-MM-ddTHH:mm:ss.fffffffffzzz"
                };

        public static readonly string TsuDefault = "yyyy-MM-ddTHH:mm:ss.fffZ";
        public static readonly string[] Tsu = JsonNetTimeFormats.Tsz;
    }

    class JsonNetDateConverter : IsoDateTimeConverter {
        public JsonNetDateConverter() {
            base.DateTimeFormat = "yyyy-MM-dd";
        }
    }

    class JsonNetTimeConverter : IsoDateTimeConverter {
        public JsonNetTimeConverter() {
            base.DateTimeFormat = "HH:mm:ss.fff";
        }
    }

    class JsonNetDateTimeLocalConverter : IsoDateTimeConverter {
        public JsonNetDateTimeLocalConverter() {
            base.DateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fff";
        }
    }

    class JsonNetDateTimeZonedConverter : IsoDateTimeConverter {
        public JsonNetDateTimeZonedConverter() {
            base.DateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffzzz";
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy