Adds MessagePack serialization for nucache
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
|
||||
internal class JsonContentNestedDataSerializer : IContentNestedDataSerializer
|
||||
{
|
||||
public ContentNestedData Deserialize(string data)
|
||||
@@ -17,7 +18,13 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
Converters = new List<JsonConverter> { new ForceInt32Converter() }
|
||||
Converters = new List<JsonConverter> { new ForceInt32Converter() },
|
||||
|
||||
// Explicitly specify date handling so that it's consistent and follows the same date handling as MessagePack
|
||||
DateParseHandling = DateParseHandling.DateTime,
|
||||
DateFormatHandling = DateFormatHandling.IsoDateFormat,
|
||||
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
||||
DateFormatString = "o"
|
||||
};
|
||||
|
||||
return JsonConvert.DeserializeObject<ContentNestedData>(data, settings);
|
||||
@@ -25,6 +32,9 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
|
||||
public string Serialize(ContentNestedData nestedData)
|
||||
{
|
||||
// note that numeric values (which are Int32) are serialized without their
|
||||
// type (eg "value":1234) and JsonConvert by default deserializes them as Int64
|
||||
|
||||
return JsonConvert.SerializeObject(nestedData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using MessagePack;
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
internal class MsgPackContentNestedDataSerializer : IContentNestedDataSerializer
|
||||
{
|
||||
private MessagePackSerializerOptions _options;
|
||||
|
||||
public MsgPackContentNestedDataSerializer()
|
||||
{
|
||||
_options = MessagePack.Resolvers.ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.Lz4BlockArray);
|
||||
}
|
||||
|
||||
public string ToJson(string serialized)
|
||||
{
|
||||
var bin = Convert.FromBase64String(serialized);
|
||||
var json = MessagePackSerializer.ConvertToJson(bin, _options);
|
||||
return json;
|
||||
}
|
||||
|
||||
// TODO: Instead of returning base64 it would be more ideal to avoid that translation entirely and just store/retrieve raw bytes
|
||||
|
||||
// TODO: We need to write tests to serialize/deserialize between either of these serializers to ensure we end up with the same object
|
||||
// i think this one is a bit quirky so far :)
|
||||
|
||||
public ContentNestedData Deserialize(string data)
|
||||
{
|
||||
var bin = Convert.FromBase64String(data);
|
||||
var obj = MessagePackSerializer.Deserialize<ContentNestedData>(bin, _options);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public string Serialize(ContentNestedData nestedData)
|
||||
{
|
||||
var bin = MessagePackSerializer.Serialize(
|
||||
nestedData,
|
||||
_options);
|
||||
return Convert.ToBase64String(bin);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,16 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
internal abstract class SerializerBase
|
||||
{
|
||||
private const char PrefixNull = 'N';
|
||||
private const char PrefixString = 'S';
|
||||
private const char PrefixInt32 = 'I';
|
||||
private const char PrefixUInt16 = 'H';
|
||||
private const char PrefixLong = 'L';
|
||||
private const char PrefixFloat = 'F';
|
||||
private const char PrefixDouble = 'B';
|
||||
private const char PrefixDateTime = 'D';
|
||||
private const char PrefixByte = 'O';
|
||||
|
||||
protected string ReadString(Stream stream) => PrimitiveSerializer.String.ReadFrom(stream);
|
||||
protected int ReadInt(Stream stream) => PrimitiveSerializer.Int32.ReadFrom(stream);
|
||||
protected long ReadLong(Stream stream) => PrimitiveSerializer.Int64.ReadFrom(stream);
|
||||
@@ -17,7 +27,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
where T : struct
|
||||
{
|
||||
var type = PrimitiveSerializer.Char.ReadFrom(stream);
|
||||
if (type == 'N') return null;
|
||||
if (type == PrefixNull) return null;
|
||||
if (type != t)
|
||||
throw new NotSupportedException($"Cannot deserialize type '{type}', expected '{t}'.");
|
||||
return read(stream);
|
||||
@@ -26,40 +36,47 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
protected string ReadStringObject(Stream stream, bool intern = false) // required 'cos string is not a struct
|
||||
{
|
||||
var type = PrimitiveSerializer.Char.ReadFrom(stream);
|
||||
if (type == 'N') return null;
|
||||
if (type != 'S')
|
||||
if (type == PrefixNull) return null;
|
||||
if (type != PrefixString)
|
||||
throw new NotSupportedException($"Cannot deserialize type '{type}', expected 'S'.");
|
||||
return intern
|
||||
? string.Intern(PrimitiveSerializer.String.ReadFrom(stream))
|
||||
: PrimitiveSerializer.String.ReadFrom(stream);
|
||||
}
|
||||
|
||||
protected int? ReadIntObject(Stream stream) => ReadObject(stream, 'I', ReadInt);
|
||||
protected long? ReadLongObject(Stream stream) => ReadObject(stream, 'L', ReadLong);
|
||||
protected float? ReadFloatObject(Stream stream) => ReadObject(stream, 'F', ReadFloat);
|
||||
protected double? ReadDoubleObject(Stream stream) => ReadObject(stream, 'B', ReadDouble);
|
||||
protected DateTime? ReadDateTimeObject(Stream stream) => ReadObject(stream, 'D', ReadDateTime);
|
||||
protected int? ReadIntObject(Stream stream) => ReadObject(stream, PrefixInt32, ReadInt);
|
||||
protected long? ReadLongObject(Stream stream) => ReadObject(stream, PrefixLong, ReadLong);
|
||||
protected float? ReadFloatObject(Stream stream) => ReadObject(stream, PrefixFloat, ReadFloat);
|
||||
protected double? ReadDoubleObject(Stream stream) => ReadObject(stream, PrefixDouble, ReadDouble);
|
||||
protected DateTime? ReadDateTimeObject(Stream stream) => ReadObject(stream, PrefixDateTime, ReadDateTime);
|
||||
|
||||
protected object ReadObject(Stream stream)
|
||||
=> ReadObject(PrimitiveSerializer.Char.ReadFrom(stream), stream);
|
||||
|
||||
protected object ReadObject(char type, Stream stream)
|
||||
{
|
||||
// NOTE: There is going to be a ton of boxing going on here, but i'm not sure we can avoid that because innevitably with our
|
||||
// current model structure the value will need to end up being 'object' at some point anyways.
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 'N':
|
||||
case PrefixNull:
|
||||
return null;
|
||||
case 'S':
|
||||
case PrefixString:
|
||||
return PrimitiveSerializer.String.ReadFrom(stream);
|
||||
case 'I':
|
||||
case PrefixInt32:
|
||||
return PrimitiveSerializer.Int32.ReadFrom(stream);
|
||||
case 'L':
|
||||
case PrefixUInt16:
|
||||
return PrimitiveSerializer.UInt16.ReadFrom(stream);
|
||||
case PrefixByte:
|
||||
return PrimitiveSerializer.Byte.ReadFrom(stream);
|
||||
case PrefixLong:
|
||||
return PrimitiveSerializer.Int64.ReadFrom(stream);
|
||||
case 'F':
|
||||
case PrefixFloat:
|
||||
return PrimitiveSerializer.Float.ReadFrom(stream);
|
||||
case 'B':
|
||||
case PrefixDouble:
|
||||
return PrimitiveSerializer.Double.ReadFrom(stream);
|
||||
case 'D':
|
||||
case PrefixDateTime:
|
||||
return PrimitiveSerializer.DateTime.ReadFrom(stream);
|
||||
default:
|
||||
throw new NotSupportedException($"Cannot deserialize unknown type '{type}'.");
|
||||
@@ -70,36 +87,46 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
PrimitiveSerializer.Char.WriteTo('N', stream);
|
||||
PrimitiveSerializer.Char.WriteTo(PrefixNull, stream);
|
||||
}
|
||||
else if (value is string stringValue)
|
||||
{
|
||||
PrimitiveSerializer.Char.WriteTo('S', stream);
|
||||
PrimitiveSerializer.Char.WriteTo(PrefixString, stream);
|
||||
PrimitiveSerializer.String.WriteTo(stringValue, stream);
|
||||
}
|
||||
else if (value is int intValue)
|
||||
{
|
||||
PrimitiveSerializer.Char.WriteTo('I', stream);
|
||||
PrimitiveSerializer.Char.WriteTo(PrefixInt32, stream);
|
||||
PrimitiveSerializer.Int32.WriteTo(intValue, stream);
|
||||
}
|
||||
else if (value is byte byteValue)
|
||||
{
|
||||
PrimitiveSerializer.Char.WriteTo(PrefixByte, stream);
|
||||
PrimitiveSerializer.Byte.WriteTo(byteValue, stream);
|
||||
}
|
||||
else if (value is ushort ushortValue)
|
||||
{
|
||||
PrimitiveSerializer.Char.WriteTo(PrefixUInt16, stream);
|
||||
PrimitiveSerializer.UInt16.WriteTo(ushortValue, stream);
|
||||
}
|
||||
else if (value is long longValue)
|
||||
{
|
||||
PrimitiveSerializer.Char.WriteTo('L', stream);
|
||||
PrimitiveSerializer.Char.WriteTo(PrefixLong, stream);
|
||||
PrimitiveSerializer.Int64.WriteTo(longValue, stream);
|
||||
}
|
||||
else if (value is float floatValue)
|
||||
{
|
||||
PrimitiveSerializer.Char.WriteTo('F', stream);
|
||||
PrimitiveSerializer.Char.WriteTo(PrefixFloat, stream);
|
||||
PrimitiveSerializer.Float.WriteTo(floatValue, stream);
|
||||
}
|
||||
else if (value is double doubleValue)
|
||||
{
|
||||
PrimitiveSerializer.Char.WriteTo('B', stream);
|
||||
PrimitiveSerializer.Char.WriteTo(PrefixDouble, stream);
|
||||
PrimitiveSerializer.Double.WriteTo(doubleValue, stream);
|
||||
}
|
||||
else if (value is DateTime dateValue)
|
||||
{
|
||||
PrimitiveSerializer.Char.WriteTo('D', stream);
|
||||
PrimitiveSerializer.Char.WriteTo(PrefixDateTime, stream);
|
||||
PrimitiveSerializer.DateTime.WriteTo(dateValue, stream);
|
||||
}
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user