Skip to content

Commit

Permalink
Some more serialization changes.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
zapov committed Oct 16, 2014
1 parent 006fa0c commit 6bf3668
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 157 deletions.
237 changes: 134 additions & 103 deletions Code/Core/Revenj.Serialization/Json/JsonSerialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,84 +39,63 @@ public JsonSerialization(

public string Serialize<T>(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<T>(string data, StreamingContext context)
{
var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Auto;
settings.Context = context;
settings.Binder = Binder;
try
{
return (T)JsonConvert.DeserializeObject<T>(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<T>(new StringReader(data), context);
}

TextReader ISerialization<TextReader>.Serialize<T>(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<T>(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
Expand All @@ -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;
Expand Down Expand Up @@ -231,46 +198,97 @@ public void SerializeJsonObject(object value, Stream s)
sw.Flush();
}

private static ConcurrentDictionary<Type, IJsonObject> Cache = new ConcurrentDictionary<Type, IJsonObject>(1, 17);
private IJsonObject GetSerializer(Type target)
private static ConcurrentDictionary<Type, IDeserializer> Cache = new ConcurrentDictionary<Type, IDeserializer>(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<IJsonObject>).IsAssignableFrom(target))
jo = (IJsonObject)Activator.CreateInstance(target.GetGenericArguments()[0]);
else if (typeof(ICollection<IJsonObject>).IsAssignableFrom(target))
jo = (IJsonObject)Activator.CreateInstance(target.GetGenericArguments()[0]);
Cache.TryAdd(target, jo);
return jo;
type = target.GetElementType();
else if (target.IsGenericType && typeof(ICollection<IJsonObject>).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<T> : 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<IJsonObject>).IsAssignableFrom(Target);
IsSet = typeof(ISet<IJsonObject>).IsAssignableFrom(Target);
IsStack = typeof(Stack<T>) == Target;
IsQueue = typeof(Queue<T>) == Target;
IsLinkedList = typeof(LinkedList<T>) == 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<T>).ToArray();
if (IsSet)
return new HashSet<T>(result as List<T>);
if (IsList)
return result;
if (IsStack)
return new Stack<T>(result as List<T>);
if (IsQueue)
return new Queue<T>(result as List<T>);
if (IsLinkedList)
return new LinkedList<T>(result as List<T>);
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)
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions Code/Core/Revenj.Serialization/WireSerialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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"))
Expand All @@ -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);
}
Expand Down
7 changes: 7 additions & 0 deletions Code/Core/Revenj.Utility/Streams/BufferedTextReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class BufferedTextReader : TextReader
private int InBuffer;
private int BufferEnd;
private int NextChar;
private int TotalBuffersRead;

public BufferedTextReader(TextReader reader)
{
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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)
Expand All @@ -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;
Expand Down
Loading

0 comments on commit 6bf3668

Please sign in to comment.