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
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 NETSTANDARD |
|
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('\"'); |
|
} |
|
} |
|
} |