From 6bf366822117236d6a280d291a1fb00a0939c53f Mon Sep 17 00:00:00 2001 From: Rikard Pavelic Date: Thu, 16 Oct 2014 10:08:40 +0200 Subject: [PATCH] Some more serialization changes. Activate baked in serialization and deserialization by default. Support collections for baked in deserialization. Fixed global error handler for requests. Updated Json.NET to support TextReader --- .../Json/JsonSerialization.cs | 237 ++++++++++-------- .../Revenj.Serialization/WireSerialization.cs | 8 +- .../Streams/BufferedTextReader.cs | 7 + Code/Server/Revenj.Wcf/GlobalErrorHandler.cs | 6 +- .../Revenj.Wcf/Rest/JsonCommandDescription.cs | 16 +- .../Server/Revenj.Wcf/Rest/RestApplication.cs | 50 +--- Dependencies/Json.NET/Newtonsoft.Json.dll | Bin 429056 -> 429056 bytes 7 files changed, 167 insertions(+), 157 deletions(-) diff --git a/Code/Core/Revenj.Serialization/Json/JsonSerialization.cs b/Code/Core/Revenj.Serialization/Json/JsonSerialization.cs index 83862449..94bde333 100644 --- a/Code/Core/Revenj.Serialization/Json/JsonSerialization.cs +++ b/Code/Core/Revenj.Serialization/Json/JsonSerialization.cs @@ -39,84 +39,63 @@ public JsonSerialization( public string Serialize(T value) { - var declaredType = typeof(T); - var type = value != null ? value.GetType() : declaredType; - var settings = new JsonSerializerSettings(); - settings.Converters.Add(new StringEnumConverter()); - settings.TypeNameHandling = type != declaredType || !(declaredType.IsClass || declaredType.IsValueType) ? TypeNameHandling.Objects : TypeNameHandling.Auto; - settings.Binder = Binder; - return JsonConvert.SerializeObject(value, settings); + using (var cms = ChunkedMemoryStream.Create()) + { + Serialize(value, cms); + cms.Position = 0; + return cms.GetReader().ReadToEnd(); + } } public T Deserialize(string data, StreamingContext context) { - var settings = new JsonSerializerSettings(); - settings.TypeNameHandling = TypeNameHandling.Auto; - settings.Context = context; - settings.Binder = Binder; - try - { - return (T)JsonConvert.DeserializeObject(data, settings); - } - catch (TargetInvocationException tex) - { - if (tex.InnerException != null) - throw tex.InnerException; - throw; - } - catch (JsonSerializationException ex) - { - Logger.Trace(ex.ToString()); - Logger.Trace(data); - throw; - } + return Deserialize(new StringReader(data), context); } TextReader ISerialization.Serialize(T value) { - var serializer = new JsonSerializer(); - serializer.Converters.Add(new StringEnumConverter()); - var declaredType = typeof(T); - var type = value != null ? value.GetType() : declaredType; - serializer.TypeNameHandling = type != declaredType || !(declaredType.IsClass || declaredType.IsValueType) ? TypeNameHandling.Objects : TypeNameHandling.Auto; - serializer.Binder = Binder; var cms = ChunkedMemoryStream.Create(); - var sw = cms.GetWriter(); - serializer.Serialize(sw, value); - sw.Flush(); + Serialize(value, cms); cms.Position = 0; return cms.GetReader(); } public T Deserialize(TextReader data, StreamingContext context) { - var serializer = new JsonSerializer(); - serializer.TypeNameHandling = TypeNameHandling.Auto; - serializer.Context = context; - serializer.Binder = Binder; - try - { - return (T)serializer.Deserialize(data, typeof(T)); - } - catch (TargetInvocationException tex) + return (T)DeserializeReader(data, typeof(T), context); + } + + public object Deserialize(Stream stream, Type type, StreamingContext context) + { + TextReader reader; + var cms = stream as ChunkedMemoryStream; + if (cms != null) + reader = cms.GetReader(); + else + reader = new StreamReader(stream, Encoding.UTF8); + return DeserializeReader(reader, type, context); + } + + private object DeserializeReader(TextReader reader, Type type, StreamingContext context) + { + if (MoveToFirstToken(reader) == 'n') { - if (tex.InnerException != null) - throw tex.InnerException; - throw; + if (reader.Read() == 'n' && reader.Read() == 'u' && reader.Read() == 'l' && reader.Read() == 'l') + return null; + throw new SerializationException("Expecting null, but found: " + (char)reader.Peek() + " at " + PositionInStream(reader)); } - catch (JsonSerializationException ex) + var deserializer = GetDeserializer(type); + if (deserializer == null) { - throw; + if (context.Context == null) + return SharedSerializer.Deserialize(reader, type); + var serializer = new JsonSerializer(); + serializer.TypeNameHandling = TypeNameHandling.Auto; + serializer.Context = context; + serializer.Binder = Binder; + return serializer.Deserialize(reader, type); } - } - - public object DeserializeJson(Stream stream, Type type, StreamingContext context) - { - var serializer = new JsonSerializer(); - serializer.TypeNameHandling = TypeNameHandling.Auto; - serializer.Context = context; - serializer.Binder = Binder; - return serializer.Deserialize(new StreamReader(stream, Encoding.UTF8), type); + return deserializer.Deserialize(reader, context); } class InvariantWriter : StreamWriter @@ -129,18 +108,6 @@ public InvariantWriter(Stream s) : base(s) { } } public void Serialize(object value, Stream s) - { - TextWriter sw; - var cms = s as ChunkedMemoryStream; - if (cms != null) - sw = cms.GetWriter(); - else - sw = new InvariantWriter(s); - SharedSerializer.Serialize(sw, value); - sw.Flush(); - } - - public void SerializeJsonObject(object value, Stream s) { TextWriter sw; var cms = s as ChunkedMemoryStream; @@ -231,46 +198,97 @@ public void SerializeJsonObject(object value, Stream s) sw.Flush(); } - private static ConcurrentDictionary Cache = new ConcurrentDictionary(1, 17); - private IJsonObject GetSerializer(Type target) + private static ConcurrentDictionary Cache = new ConcurrentDictionary(1, 17); + private IDeserializer GetDeserializer(Type target) { - IJsonObject jo = null; - if (Cache.TryGetValue(target, out jo)) - return jo; + IDeserializer des = null; + if (Cache.TryGetValue(target, out des)) + return des; + Type type = null; if (typeof(IJsonObject).IsAssignableFrom(target)) - jo = (IJsonObject)Activator.CreateInstance(target); + type = target; else if (typeof(IJsonObject[]).IsAssignableFrom(target)) - jo = (IJsonObject)Activator.CreateInstance(target.GetElementType()); - else if (typeof(IList).IsAssignableFrom(target)) - jo = (IJsonObject)Activator.CreateInstance(target.GetGenericArguments()[0]); - else if (typeof(ICollection).IsAssignableFrom(target)) - jo = (IJsonObject)Activator.CreateInstance(target.GetGenericArguments()[0]); - Cache.TryAdd(target, jo); - return jo; + type = target.GetElementType(); + else if (target.IsGenericType && typeof(ICollection).IsAssignableFrom(target)) + type = target.GetGenericArguments()[0]; + if (type != null && typeof(IJsonObject).IsAssignableFrom(type)) + { + var desType = typeof(Deserializer<>).MakeGenericType(type); + des = (IDeserializer)Activator.CreateInstance(desType, new object[] { target, SharedSerializer, Binder }); + } + Cache.TryAdd(target, des); + return des; } - public object Deserialize(Stream s, Type target, StreamingContext context) + interface IDeserializer { - var serializer = new JsonSerializer(); - serializer.TypeNameHandling = TypeNameHandling.Auto; - serializer.Context = context; - serializer.Binder = Binder; - TextReader sr; - var cms = s as ChunkedMemoryStream; - if (cms != null) - sr = cms.GetReader(); - else - sr = new StreamReader(s, Encoding.UTF8); - if (sr.Peek() == 'n') + object Deserialize(TextReader reader, StreamingContext context); + } + class Deserializer : IDeserializer + where T : IJsonObject + { + private readonly IJsonObject Converter; + private readonly JsonSerializer JsonNet; + private readonly GenericDeserializationBinder Binder; + private readonly Type Target; + private readonly Type Type; + private readonly bool IsSimple; + private readonly bool IsArray; + private readonly bool IsList; + private readonly bool IsSet; + private readonly bool IsStack; + private readonly bool IsQueue; + private readonly bool IsLinkedList; + + public Deserializer(Type target, JsonSerializer jsonNet, GenericDeserializationBinder binder) { - if (sr.Read() == 'n' && sr.Read() == 'u' && sr.Read() == 'l' && sr.Read() == 'l') - return null; - throw new SerializationException("Invalid json provided"); + this.JsonNet = jsonNet; + this.Binder = binder; + this.Target = target; + this.Type = typeof(T); + try + { + Converter = (IJsonObject)FormatterServices.GetUninitializedObject(Type); + IsSimple = Type == Target; + IsArray = Target.IsArray; + IsList = typeof(IList).IsAssignableFrom(Target); + IsSet = typeof(ISet).IsAssignableFrom(Target); + IsStack = typeof(Stack) == Target; + IsQueue = typeof(Queue) == Target; + IsLinkedList = typeof(LinkedList) == Target; + } + catch { } + } + + public object Deserialize(TextReader reader, StreamingContext context) + { + if (Converter == null) + { + if (context.Context == null) + return JsonNet.Deserialize(reader, Target); + var serializer = new JsonSerializer(); + serializer.TypeNameHandling = TypeNameHandling.Auto; + serializer.Context = context; + serializer.Binder = Binder; + return serializer.Deserialize(reader, Target); + } + var result = Converter.Deserialize(reader, context, JsonNet.Deserialize); + if (IsSimple) + return result; + if (IsArray) + return (result as List).ToArray(); + if (IsSet) + return new HashSet(result as List); + if (IsList) + return result; + if (IsStack) + return new Stack(result as List); + if (IsQueue) + return new Queue(result as List); + if (IsLinkedList) + return new LinkedList(result as List); + return result; } - var ser = GetSerializer(target); - if (ser == null) - return serializer.Deserialize(sr, target); - return ser.Deserialize(sr, context, serializer.Deserialize); } private static bool IsWhiteSpace(int c) @@ -315,6 +333,17 @@ public static int GetNextToken(TextReader sr) return c; } + public static int MoveToFirstToken(TextReader sr) + { + int c = sr.Peek(); + while (IsWhiteSpace(c)) + { + sr.Read(); + c = sr.Peek(); + } + return c; + } + public static int MoveToNextToken(TextReader sr, int nextToken) { int c = nextToken; @@ -335,8 +364,10 @@ public static bool SameName(char[] buffer, string name) public static long PositionInStream(TextReader tr) { var sr = tr as StreamReader; + var btr = tr as BufferedTextReader; try { + if (btr != null) return btr.Position; var binding = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField; if (sr != null) diff --git a/Code/Core/Revenj.Serialization/WireSerialization.cs b/Code/Core/Revenj.Serialization/WireSerialization.cs index 2c270462..75811945 100644 --- a/Code/Core/Revenj.Serialization/WireSerialization.cs +++ b/Code/Core/Revenj.Serialization/WireSerialization.cs @@ -27,7 +27,7 @@ public string Serialize(object value, string accept, Stream destination) //fast path if (accept == "application/json") { - Json.SerializeJsonObject(value, destination); + Json.Serialize(value, destination); return "application/json"; } if (accept == "application/x-protobuf") @@ -44,7 +44,7 @@ public string Serialize(object value, string accept, Stream destination) accept = (accept ?? "application/json").ToLowerInvariant(); if (accept.Contains("application/json")) { - Json.SerializeJsonObject(value, destination); + Json.Serialize(value, destination); return "application/json"; } if (accept.Contains("application/x-protobuf")) @@ -69,9 +69,9 @@ public object Deserialize(Stream source, Type target, string contentType, Stream return Xml.Deserialize(source, target, context); //slow path contentType = (contentType ?? "application/json").ToLowerInvariant().TrimStart(); - if (contentType.StartsWith("application/json", StringComparison.InvariantCulture)) + if (contentType.Contains("application/json")) return Json.Deserialize(source, target, context); - if (contentType.StartsWith("application/x-protobuf", StringComparison.InvariantCulture)) + if (contentType.Contains("application/x-protobuf")) return Protobuf.Deserialize(source, target, context); return Xml.Deserialize(source, target, context); } diff --git a/Code/Core/Revenj.Utility/Streams/BufferedTextReader.cs b/Code/Core/Revenj.Utility/Streams/BufferedTextReader.cs index 77608a83..e28ef39b 100644 --- a/Code/Core/Revenj.Utility/Streams/BufferedTextReader.cs +++ b/Code/Core/Revenj.Utility/Streams/BufferedTextReader.cs @@ -10,6 +10,7 @@ public class BufferedTextReader : TextReader private int InBuffer; private int BufferEnd; private int NextChar; + private int TotalBuffersRead; public BufferedTextReader(TextReader reader) { @@ -18,11 +19,14 @@ public BufferedTextReader(TextReader reader) public void Initialize() { + TotalBuffersRead = 0; InBuffer = 0; BufferEnd = Reader.Read(Buffer, 0, Buffer.Length); NextChar = BufferEnd > 0 ? Buffer[0] : -1; } + public int Position { get { return TotalBuffersRead + InBuffer; } } + public override int Peek() { return NextChar; @@ -34,6 +38,7 @@ public override int Read() InBuffer++; if (InBuffer == BufferEnd) { + TotalBuffersRead += BufferEnd; BufferEnd = Reader.Read(Buffer, 0, Buffer.Length); InBuffer = 0; NextChar = BufferEnd > 0 ? Buffer[0] : -1; @@ -49,6 +54,7 @@ public int ReadUntil(char[] target, int from, char match) { if (InBuffer == BufferEnd) { + TotalBuffersRead += BufferEnd; BufferEnd = Reader.Read(Buffer, 0, Buffer.Length); InBuffer = 0; if (BufferEnd == 0) @@ -60,6 +66,7 @@ public int ReadUntil(char[] target, int from, char match) target[j] = Buffer[i]; if (i == BufferEnd) { + TotalBuffersRead += BufferEnd; BufferEnd = Reader.Read(Buffer, 0, Buffer.Length); InBuffer = 0; NextChar = BufferEnd > 0 ? Buffer[0] : -1; diff --git a/Code/Server/Revenj.Wcf/GlobalErrorHandler.cs b/Code/Server/Revenj.Wcf/GlobalErrorHandler.cs index d439a412..5cc4b937 100644 --- a/Code/Server/Revenj.Wcf/GlobalErrorHandler.cs +++ b/Code/Server/Revenj.Wcf/GlobalErrorHandler.cs @@ -62,19 +62,19 @@ public void ProvideFault( { ErrorLogger.Trace(() => fe.Message); if (fault == null) - fault = CreateError(version, se.Message, HttpStatusCode.BadRequest); + fault = CreateError(version, fe.Message, HttpStatusCode.BadRequest); } else if (anse != null) { ErrorLogger.Trace(() => anse.Message); if (fault == null) - fault = CreateError(version, se.Message, HttpStatusCode.NotFound); + fault = CreateError(version, anse.Message, HttpStatusCode.NotFound); } else { ErrorLogger.Error(error.GetDetailedExplanation()); if (fault == null) - fault = CreateError(version, se.Message, HttpStatusCode.InternalServerError); + fault = CreateError(version, error.Message, HttpStatusCode.InternalServerError); } } diff --git a/Code/Server/Revenj.Wcf/Rest/JsonCommandDescription.cs b/Code/Server/Revenj.Wcf/Rest/JsonCommandDescription.cs index 7de4bacd..f14f63eb 100644 --- a/Code/Server/Revenj.Wcf/Rest/JsonCommandDescription.cs +++ b/Code/Server/Revenj.Wcf/Rest/JsonCommandDescription.cs @@ -4,13 +4,14 @@ using System.Linq; using System.Text; using Revenj.Processing; +using Revenj.Utility; namespace Revenj.Wcf { - public class JsonCommandDescription : IServerCommandDescription + public class JsonCommandDescription : IServerCommandDescription { public string RequestID { get; private set; } - public StreamReader Data { get; set; } + public TextReader Data { get; set; } public Type CommandType { get; private set; } public JsonCommandDescription(NameValueCollection args, Stream message, Type commandType) @@ -18,7 +19,11 @@ public JsonCommandDescription(NameValueCollection args, Stream message, Type com this.CommandType = commandType; if (message != null) { - Data = new StreamReader(message, Encoding.UTF8); + var cms = message as ChunkedMemoryStream; + if (cms != null) + Data = cms.GetReader(); + else + Data = new StreamReader(message, Encoding.UTF8); } else if (args != null && args.Count > 0) { @@ -32,9 +37,8 @@ from key in args.AllKeys let isArr = val.Contains(',') let arrVal = isArr ? string.Join(",", val.Split(',').Where(it => it.Length > 0).Select(it => "\"{0}\"".With(it))) : null select "\"{0}\": ".With(key) + (isArr ? "[" + arrVal + "]" : "\"{0}\"".With(val)); - var ms = new MemoryStream(Encoding.UTF8.GetBytes(start + string.Join(@", - ", properties) + end)); - Data = new StreamReader(ms); + Data = new StringReader(start + string.Join(@", + ", properties) + end); } } } diff --git a/Code/Server/Revenj.Wcf/Rest/RestApplication.cs b/Code/Server/Revenj.Wcf/Rest/RestApplication.cs index edfb5a3c..073d4d0e 100644 --- a/Code/Server/Revenj.Wcf/Rest/RestApplication.cs +++ b/Code/Server/Revenj.Wcf/Rest/RestApplication.cs @@ -136,30 +136,7 @@ internal static Stream ExecuteCommand( IServerCommandDescription command, string accept) { - if (accept == "application/json-experimental") - { - var instance = Execute(engine, command); - if (instance.Error != null) - return instance.Error; - if (instance.Result == null) - return null; - var cms = ChunkedMemoryStream.Create(); - var ct = serialization.Serialize(instance.Result, accept, cms); - ThreadContext.Response.ContentType = ct; - cms.Position = 0; - return cms; - } - if (accept.Contains("application/json")) - { - var json = Execute(engine, command); - if (json.Error != null) - return json.Error; - ThreadContext.Response.ContentType = "application/json"; - if (json.Result == null) - return null; - return json.Result.BaseStream; - } - if (accept.Contains("application/octet-stream")) + if (accept == "application/octet-stream") { var native = Execute(engine, command); if (native.Error != null) @@ -193,7 +170,7 @@ internal static Stream ExecuteCommand( + native.Result.GetType().FullName + " to octet-stream. Use application/x-dotnet mime type for .NET binary serialization", HttpStatusCode.BadRequest); } - if (accept.Contains("application/base64")) + if (accept == "application/base64") { var native = Execute(engine, command); if (native.Error != null) @@ -244,17 +221,9 @@ internal static Stream ExecuteCommand( sb.Append(ch); return sb.ToBase64Stream(); } - return Utility.ReturnError("Unexpected command result. Cant convert to base64.", HttpStatusCode.BadRequest); - } - if (accept.Contains("application/x-protobuf")) - { - var proto = Execute(engine, command); - if (proto.Error != null) - return proto.Error; - ThreadContext.Response.ContentType = "application/x-protobuf"; - return proto.Result; + return Utility.ReturnError("Unexpected command result. Can't convert to base64.", HttpStatusCode.BadRequest); } - if (accept.Contains("application/x-dotnet")) + if (accept == "application/x-dotnet") { var native = Execute(engine, command); if (native.Error != null) @@ -269,14 +238,13 @@ internal static Stream ExecuteCommand( cms.Position = 0; return cms; } - var xml = Execute(engine, command); - if (xml.Error != null) - return xml.Error; - ThreadContext.Response.ContentType = "application/xml"; - if (xml.Result == null) + var instance = Execute(engine, command); + if (instance.Error != null) + return instance.Error; + if (instance.Result == null) return null; var ms = ChunkedMemoryStream.Create(); - xml.Result.Save(ms); + ThreadContext.Response.ContentType = serialization.Serialize(instance.Result, accept, ms); ms.Position = 0; return ms; } diff --git a/Dependencies/Json.NET/Newtonsoft.Json.dll b/Dependencies/Json.NET/Newtonsoft.Json.dll index d0a997088fcf86c8a7d878762b3aba60187cc7dc..376dce40e4ea433f0ab2c1e2fa89c5caa83a26fb 100644 GIT binary patch delta 17759 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