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 { /// /// Dump to JSON string. /// public static string ToJson(T obj) { return ToJson(Serialize(obj)); } /// /// Dump to JSON string. /// public static string ToJson(T obj, IFormatterResolver resolver) { return ToJson(Serialize(obj, resolver)); } /// /// Dump message-pack binary to JSON string. /// 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); } } /// /// From Json String to MessagePack binary /// 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; } /// /// return buffer is from memory pool, be careful to use. /// internal static ArraySegment 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(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('\"'); } } }