You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
16 KiB
362 lines
16 KiB
1 year ago
|
using MessagePack.Formatters;
|
||
|
using MessagePack.Internal;
|
||
|
using System;
|
||
|
using System.Globalization;
|
||
|
using System.IO;
|
||
|
using System.Text;
|
||
|
|
||
|
namespace MessagePack
|
||
|
{
|
||
|
// JSON API
|
||
|
public static partial class MessagePackSerializer
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Dump to JSON string.
|
||
|
/// </summary>
|
||
|
public static string ToJson<T>(T obj)
|
||
|
{
|
||
|
return ToJson(Serialize(obj));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Dump to JSON string.
|
||
|
/// </summary>
|
||
|
public static string ToJson<T>(T obj, IFormatterResolver resolver)
|
||
|
{
|
||
|
return ToJson(Serialize(obj, resolver));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Dump message-pack binary to JSON string.
|
||
|
/// </summary>
|
||
|
public static string ToJson(byte[] bytes)
|
||
|
{
|
||
|
if (bytes == null || bytes.Length == 0) return "";
|
||
|
|
||
|
var sb = new StringBuilder();
|
||
|
ToJsonCore(bytes, 0, sb);
|
||
|
return sb.ToString();
|
||
|
}
|
||
|
|
||
|
public static byte[] FromJson(string str)
|
||
|
{
|
||
|
using (var sr = new StringReader(str))
|
||
|
{
|
||
|
return FromJson(sr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// From Json String to MessagePack binary
|
||
|
/// </summary>
|
||
|
public static byte[] FromJson(TextReader reader)
|
||
|
{
|
||
|
var offset = 0;
|
||
|
byte[] binary = null;
|
||
|
using (var jr = new TinyJsonReader(reader, false))
|
||
|
{
|
||
|
FromJsonCore(jr, ref binary, ref offset);
|
||
|
}
|
||
|
MessagePackBinary.FastResize(ref binary, offset);
|
||
|
return binary;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// return buffer is from memory pool, be careful to use.
|
||
|
/// </summary>
|
||
|
internal static ArraySegment<byte> FromJsonUnsafe(TextReader reader)
|
||
|
{
|
||
|
var offset = 0;
|
||
|
byte[] binary = InternalMemoryPool.GetBuffer(); // from memory pool.
|
||
|
using (var jr = new TinyJsonReader(reader, false))
|
||
|
{
|
||
|
FromJsonCore(jr, ref binary, ref offset);
|
||
|
}
|
||
|
return new ArraySegment<byte>(binary, 0, offset);
|
||
|
}
|
||
|
|
||
|
static uint FromJsonCore(TinyJsonReader jr, ref byte[] binary, ref int offset)
|
||
|
{
|
||
|
uint count = 0;
|
||
|
while (jr.Read())
|
||
|
{
|
||
|
switch (jr.TokenType)
|
||
|
{
|
||
|
case TinyJsonToken.None:
|
||
|
break;
|
||
|
case TinyJsonToken.StartObject:
|
||
|
{
|
||
|
var startOffset = offset;
|
||
|
offset += 5;
|
||
|
var mapCount = FromJsonCore(jr, ref binary, ref offset);
|
||
|
mapCount = mapCount / 2; // remove propertyname string count.
|
||
|
MessagePackBinary.WriteMapHeaderForceMap32Block(ref binary, startOffset, mapCount);
|
||
|
count++;
|
||
|
break;
|
||
|
}
|
||
|
case TinyJsonToken.EndObject:
|
||
|
return count; // break
|
||
|
case TinyJsonToken.StartArray:
|
||
|
{
|
||
|
var startOffset = offset;
|
||
|
offset += 5;
|
||
|
var arrayCount = FromJsonCore(jr, ref binary, ref offset);
|
||
|
MessagePackBinary.WriteArrayHeaderForceArray32Block(ref binary, startOffset, arrayCount);
|
||
|
count++;
|
||
|
break;
|
||
|
}
|
||
|
case TinyJsonToken.EndArray:
|
||
|
return count; // break
|
||
|
case TinyJsonToken.Number:
|
||
|
var v = jr.ValueType;
|
||
|
if (v == ValueType.Double)
|
||
|
{
|
||
|
offset += MessagePackBinary.WriteDouble(ref binary, offset, jr.DoubleValue);
|
||
|
}
|
||
|
else if (v == ValueType.Long)
|
||
|
{
|
||
|
offset += MessagePackBinary.WriteInt64(ref binary, offset, jr.LongValue);
|
||
|
}
|
||
|
else if (v == ValueType.ULong)
|
||
|
{
|
||
|
offset += MessagePackBinary.WriteUInt64(ref binary, offset, jr.ULongValue);
|
||
|
}
|
||
|
else if (v == ValueType.Decimal)
|
||
|
{
|
||
|
offset += DecimalFormatter.Instance.Serialize(ref binary, offset, jr.DecimalValue, null);
|
||
|
}
|
||
|
count++;
|
||
|
break;
|
||
|
case TinyJsonToken.String:
|
||
|
offset += MessagePackBinary.WriteString(ref binary, offset, jr.StringValue);
|
||
|
count++;
|
||
|
break;
|
||
|
case TinyJsonToken.True:
|
||
|
offset += MessagePackBinary.WriteBoolean(ref binary, offset, true);
|
||
|
count++;
|
||
|
break;
|
||
|
case TinyJsonToken.False:
|
||
|
offset += MessagePackBinary.WriteBoolean(ref binary, offset, false);
|
||
|
count++;
|
||
|
break;
|
||
|
case TinyJsonToken.Null:
|
||
|
offset += MessagePackBinary.WriteNil(ref binary, offset);
|
||
|
count++;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static int ToJsonCore(byte[] bytes, int offset, StringBuilder builder)
|
||
|
{
|
||
|
var readSize = 0;
|
||
|
var type = MessagePackBinary.GetMessagePackType(bytes, offset);
|
||
|
switch (type)
|
||
|
{
|
||
|
case MessagePackType.Integer:
|
||
|
var code = bytes[offset];
|
||
|
if (MessagePackCode.MinNegativeFixInt <= code && code <= MessagePackCode.MaxNegativeFixInt) builder.Append(MessagePackBinary.ReadSByte(bytes, offset, out readSize).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
else if (MessagePackCode.MinFixInt <= code && code <= MessagePackCode.MaxFixInt) builder.Append(MessagePackBinary.ReadByte(bytes, offset, out readSize).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
else if (code == MessagePackCode.Int8) builder.Append(MessagePackBinary.ReadSByte(bytes, offset, out readSize).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
else if (code == MessagePackCode.Int16) builder.Append(MessagePackBinary.ReadInt16(bytes, offset, out readSize).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
else if (code == MessagePackCode.Int32) builder.Append(MessagePackBinary.ReadInt32(bytes, offset, out readSize).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
else if (code == MessagePackCode.Int64) builder.Append(MessagePackBinary.ReadInt64(bytes, offset, out readSize).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
else if (code == MessagePackCode.UInt8) builder.Append(MessagePackBinary.ReadByte(bytes, offset, out readSize).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
else if (code == MessagePackCode.UInt16) builder.Append(MessagePackBinary.ReadUInt16(bytes, offset, out readSize).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
else if (code == MessagePackCode.UInt32) builder.Append(MessagePackBinary.ReadUInt32(bytes, offset, out readSize).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
else if (code == MessagePackCode.UInt64) builder.Append(MessagePackBinary.ReadUInt64(bytes, offset, out readSize).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
break;
|
||
|
case MessagePackType.Boolean:
|
||
|
builder.Append(MessagePackBinary.ReadBoolean(bytes, offset, out readSize) ? "true" : "false");
|
||
|
break;
|
||
|
case MessagePackType.Float:
|
||
|
var floatCode = bytes[offset];
|
||
|
if (floatCode == MessagePackCode.Float32)
|
||
|
{
|
||
|
builder.Append(MessagePackBinary.ReadSingle(bytes, offset, out readSize).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
builder.Append(MessagePackBinary.ReadDouble(bytes, offset, out readSize).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
}
|
||
|
break;
|
||
|
case MessagePackType.String:
|
||
|
WriteJsonString(MessagePackBinary.ReadString(bytes, offset, out readSize), builder);
|
||
|
break;
|
||
|
case MessagePackType.Binary:
|
||
|
builder.Append("\"" + Convert.ToBase64String(MessagePackBinary.ReadBytes(bytes, offset, out readSize)) + "\"");
|
||
|
break;
|
||
|
case MessagePackType.Array:
|
||
|
{
|
||
|
var length = MessagePackBinary.ReadArrayHeaderRaw(bytes, offset, out readSize);
|
||
|
var totalReadSize = readSize;
|
||
|
offset += readSize;
|
||
|
builder.Append("[");
|
||
|
for (int i = 0; i < length; i++)
|
||
|
{
|
||
|
readSize = ToJsonCore(bytes, offset, builder);
|
||
|
offset += readSize;
|
||
|
totalReadSize += readSize;
|
||
|
|
||
|
if (i != length - 1)
|
||
|
{
|
||
|
builder.Append(",");
|
||
|
}
|
||
|
}
|
||
|
builder.Append("]");
|
||
|
|
||
|
return totalReadSize;
|
||
|
}
|
||
|
case MessagePackType.Map:
|
||
|
{
|
||
|
var length = MessagePackBinary.ReadMapHeaderRaw(bytes, offset, out readSize);
|
||
|
var totalReadSize = readSize;
|
||
|
offset += readSize;
|
||
|
builder.Append("{");
|
||
|
for (int i = 0; i < length; i++)
|
||
|
{
|
||
|
// write key
|
||
|
{
|
||
|
var keyType = MessagePackBinary.GetMessagePackType(bytes, offset);
|
||
|
if (keyType == MessagePackType.String || keyType == MessagePackType.Binary)
|
||
|
{
|
||
|
readSize = ToJsonCore(bytes, offset, builder);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
builder.Append("\"");
|
||
|
readSize = ToJsonCore(bytes, offset, builder);
|
||
|
builder.Append("\"");
|
||
|
}
|
||
|
offset += readSize;
|
||
|
totalReadSize += readSize;
|
||
|
}
|
||
|
|
||
|
builder.Append(":");
|
||
|
|
||
|
// write body
|
||
|
{
|
||
|
readSize = ToJsonCore(bytes, offset, builder);
|
||
|
offset += readSize;
|
||
|
totalReadSize += readSize;
|
||
|
}
|
||
|
|
||
|
if (i != length - 1)
|
||
|
{
|
||
|
builder.Append(",");
|
||
|
}
|
||
|
}
|
||
|
builder.Append("}");
|
||
|
|
||
|
return totalReadSize;
|
||
|
}
|
||
|
case MessagePackType.Extension:
|
||
|
var extHeader = MessagePackBinary.ReadExtensionFormatHeader(bytes, offset, out readSize);
|
||
|
if (extHeader.TypeCode == ReservedMessagePackExtensionTypeCode.DateTime)
|
||
|
{
|
||
|
var dt = MessagePackBinary.ReadDateTime(bytes, offset, out readSize);
|
||
|
builder.Append("\"");
|
||
|
builder.Append(dt.ToString("o", CultureInfo.InvariantCulture));
|
||
|
builder.Append("\"");
|
||
|
}
|
||
|
#if NETSTANDARD1_4 || UNITY_5_4_OR_NEWER
|
||
|
else if (extHeader.TypeCode == TypelessFormatter.ExtensionTypeCode)
|
||
|
{
|
||
|
int startOffset = offset;
|
||
|
// prepare type name token
|
||
|
offset += 6;
|
||
|
var typeNameToken = new StringBuilder();
|
||
|
var typeNameReadSize = ToJsonCore(bytes, offset, typeNameToken);
|
||
|
offset += typeNameReadSize;
|
||
|
int startBuilderLength = builder.Length;
|
||
|
if (extHeader.Length > typeNameReadSize)
|
||
|
{
|
||
|
// object map or array
|
||
|
var typeInside = MessagePackBinary.GetMessagePackType(bytes, offset);
|
||
|
if (typeInside != MessagePackType.Array && typeInside != MessagePackType.Map)
|
||
|
builder.Append("{");
|
||
|
offset += ToJsonCore(bytes, offset, builder);
|
||
|
// insert type name token to start of object map or array
|
||
|
if (typeInside != MessagePackType.Array)
|
||
|
typeNameToken.Insert(0, "\"$type\":");
|
||
|
if (typeInside != MessagePackType.Array && typeInside != MessagePackType.Map)
|
||
|
builder.Append("}");
|
||
|
if (builder.Length - startBuilderLength > 2)
|
||
|
typeNameToken.Append(",");
|
||
|
builder.Insert(startBuilderLength + 1, typeNameToken.ToString());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
builder.Append("{\"$type\":\"" + typeNameToken.ToString() + "}");
|
||
|
}
|
||
|
readSize = offset - startOffset;
|
||
|
}
|
||
|
#endif
|
||
|
else
|
||
|
{
|
||
|
var ext = MessagePackBinary.ReadExtensionFormat(bytes, offset, out readSize);
|
||
|
builder.Append("[");
|
||
|
builder.Append(ext.TypeCode);
|
||
|
builder.Append(",");
|
||
|
builder.Append("\"");
|
||
|
builder.Append(Convert.ToBase64String(ext.Data));
|
||
|
builder.Append("\"");
|
||
|
builder.Append("]");
|
||
|
}
|
||
|
break;
|
||
|
case MessagePackType.Unknown:
|
||
|
case MessagePackType.Nil:
|
||
|
default:
|
||
|
readSize = 1;
|
||
|
builder.Append("null");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return readSize;
|
||
|
}
|
||
|
|
||
|
// escape string
|
||
|
static void WriteJsonString(string value, StringBuilder builder)
|
||
|
{
|
||
|
builder.Append('\"');
|
||
|
|
||
|
var len = value.Length;
|
||
|
for (int i = 0; i < len; i++)
|
||
|
{
|
||
|
var c = value[i];
|
||
|
switch (c)
|
||
|
{
|
||
|
case '"':
|
||
|
builder.Append("\\\"");
|
||
|
break;
|
||
|
case '\\':
|
||
|
builder.Append("\\\\");
|
||
|
break;
|
||
|
case '\b':
|
||
|
builder.Append("\\b");
|
||
|
break;
|
||
|
case '\f':
|
||
|
builder.Append("\\f");
|
||
|
break;
|
||
|
case '\n':
|
||
|
builder.Append("\\n");
|
||
|
break;
|
||
|
case '\r':
|
||
|
builder.Append("\\r");
|
||
|
break;
|
||
|
case '\t':
|
||
|
builder.Append("\\t");
|
||
|
break;
|
||
|
default:
|
||
|
builder.Append(c);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
builder.Append('\"');
|
||
|
}
|
||
|
}
|
||
|
}
|