#region Header /** * JsonMapper.cs * JSON to .Net object and object to JSON conversions. * * The authors disclaim copyright to this source code. For more details, see * the COPYING file included with this distribution. **/ #endregion using Best.HTTP.Shared.PlatformSupport.Threading; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection; using System.Threading; namespace Best.HTTP.JSON.LitJson { public interface IFastSetter { void SetBool(ReadOnlySpan property, bool value); void SetInteger(ReadOnlySpan property, long value); void SetDouble(ReadOnlySpan property, double value); void SetOther(ReadOnlySpan property, object value); } class ReadOnlyMemoryCharEqualityComparer : IEqualityComparer> { public static ReadOnlyMemoryCharEqualityComparer Comparer = new ReadOnlyMemoryCharEqualityComparer(); public bool Equals(ReadOnlyMemory x, ReadOnlyMemory y) { var eq = MemoryExtensions.Equals(x.Span, y.Span, StringComparison.OrdinalIgnoreCase); return eq; } public int GetHashCode(ReadOnlyMemory obj) { HashCode hash = new HashCode(); for (int i = 0; i < obj.Length; ++i) hash.Add(Char.ToLowerInvariant(obj.Span[i])); return hash.ToHashCode(); } } internal struct PropertyMetadata { public MemberInfo Info; public bool IsField; public Type Type; } internal struct ArrayMetadata { private Type element_type; private bool is_array; private bool is_list; public Type ElementType { get { if (element_type == null) return typeof(JsonData); return element_type; } set { element_type = value; } } public bool IsArray { get { return is_array; } set { is_array = value; } } public bool IsList { get { return is_list; } set { is_list = value; } } } internal struct ObjectMetadata { private Type element_type; private bool is_dictionary; private IDictionary, PropertyMetadata> properties; public Type ElementType { get { if (element_type == null) return typeof(JsonData); return element_type; } set { element_type = value; } } public bool IsDictionary { get { return is_dictionary; } set { is_dictionary = value; } } public IDictionary, PropertyMetadata> Properties { get { return properties; } set { properties = value; } } } internal delegate void ExporterFunc(object obj, JsonWriter writer); public delegate void ExporterFunc(T obj, JsonWriter writer); internal delegate object ImporterFunc(object input); public delegate TValue ImporterFunc(TJson input); public delegate IJsonWrapper WrapperFactory(); public sealed class JsonMapper { #region Fields private static readonly int max_nesting_depth; private static readonly IFormatProvider datetime_format; private static readonly IDictionary base_exporters_table; private static readonly IDictionary custom_exporters_table; private static readonly IDictionary> base_importers_table; private static readonly IDictionary> custom_importers_table; private static readonly IDictionary array_metadata; private static readonly ReaderWriterLockSlim array_metadata_lock = new(); private static readonly IDictionary> conv_ops; private static readonly ReaderWriterLockSlim conv_ops_lock = new(); private static readonly IDictionary object_metadata; private static readonly ReaderWriterLockSlim object_metadata_lock = new(); private static readonly IDictionary> type_properties; private static readonly ReaderWriterLockSlim type_properties_lock = new(); [ThreadStatic] private static JsonWriter static_writer; private static ConcurrentStack _readerPool = new ConcurrentStack(); private static ConcurrentDictionary, string> _stringInstanceCache = new ConcurrentDictionary, string>(ReadOnlyMemoryCharEqualityComparer.Comparer); [ThreadStatic] private static char[] _tmpProperty; private static Type int_type; private static Type long_type; private static Type ulong_type; private static Type bool_type; private static Type double_type; private static Type string_type; private static Type nullable_type; #endregion #region Constructors static JsonMapper() { max_nesting_depth = 100; array_metadata = new Dictionary(); conv_ops = new Dictionary>(); object_metadata = new Dictionary(); type_properties = new Dictionary>(); datetime_format = DateTimeFormatInfo.InvariantInfo; base_exporters_table = new Dictionary(); custom_exporters_table = new Dictionary(); base_importers_table = new Dictionary>(); custom_importers_table = new Dictionary>(); int_type = typeof(int); long_type = typeof(long); ulong_type = typeof(ulong); bool_type = typeof(bool); double_type = typeof(double); string_type = typeof(string); nullable_type = typeof(Nullable<>); RegisterBaseExporters(); RegisterBaseImporters(); } #endregion #region Private Helper Mehtods private static bool HasInterface(Type type, string name) { #if NET_4_6 Type[] interfaces = type.GetInterfaces(); foreach (var iface in interfaces) { if (iface.FullName.Equals(name, StringComparison.OrdinalIgnoreCase)) return true; } return false; #else return type.GetInterface(name, true) != null; #endif } public static PropertyInfo[] GetPublicInstanceProperties(Type type) { return type.GetProperties(); } #endregion #region Private Methods private static ArrayMetadata AddArrayMetadata(Type type) { ArrayMetadata data = default; if (array_metadata.TryGetValue(type, out data)) return data; data = new ArrayMetadata(); data.IsArray = type.IsArray; data.IsList = HasInterface(type, "System.Collections.IList"); foreach (PropertyInfo p_info in GetPublicInstanceProperties(type)) { if (p_info.Name != "Item") continue; ParameterInfo[] parameters = p_info.GetIndexParameters(); if (parameters.Length != 1) continue; if (parameters[0].ParameterType == int_type) data.ElementType = p_info.PropertyType; } using (new WriteLock(array_metadata_lock)) { try { array_metadata.Add(type, data); } catch (ArgumentException) { // ?? } } return data; } private static ObjectMetadata AddObjectMetadata(Type type) { ObjectMetadata data; if (object_metadata.TryGetValue(type, out data)) return data; data = new ObjectMetadata(); data.IsDictionary = HasInterface(type, "System.Collections.IDictionary"); data.Properties = new Dictionary, PropertyMetadata>(ReadOnlyMemoryCharEqualityComparer.Comparer); foreach (PropertyInfo p_info in GetPublicInstanceProperties(type)) { if (p_info.Name == "Item") { ParameterInfo[] parameters = p_info.GetIndexParameters(); if (parameters.Length != 1) continue; if (parameters[0].ParameterType == string_type) data.ElementType = p_info.PropertyType; continue; } PropertyMetadata p_data = new PropertyMetadata(); p_data.Info = p_info; p_data.Type = p_info.PropertyType; data.Properties.Add(p_info.Name.AsMemory(), p_data); } foreach (FieldInfo f_info in type.GetFields()) { if (!data.Properties.TryGetValue(f_info.Name.AsMemory(), out var p_data)) { p_data = new PropertyMetadata(); p_data.Info = f_info; p_data.IsField = true; p_data.Type = f_info.FieldType; data.Properties.Add(f_info.Name.AsMemory(), p_data); } } using (new WriteLock(object_metadata_lock)) { try { object_metadata.Add(type, data); } catch (ArgumentException) { // ?? } } return data; } private static List AddTypeProperties(Type type) { List props; if (type_properties.TryGetValue(type, out props)) return props; props = new List(); foreach (PropertyInfo p_info in GetPublicInstanceProperties(type)) { if (p_info.Name == "Item") continue; PropertyMetadata p_data = new PropertyMetadata(); p_data.Info = p_info; p_data.IsField = false; props.Add(p_data); } foreach (FieldInfo f_info in type.GetFields()) { PropertyMetadata p_data = new PropertyMetadata(); p_data.Info = f_info; p_data.IsField = true; props.Add(p_data); } using (new WriteLock(type_properties_lock)) { try { type_properties.Add(type, props); } catch (ArgumentException) { // ?? } } return props; } private static MethodInfo GetConvOp(Type t1, Type t2) { Dictionary ops; using (new WriteLock(conv_ops_lock)) { if (!conv_ops.TryGetValue(t1, out ops)) conv_ops.Add(t1, ops = new Dictionary()); } MethodInfo op; if (conv_ops[t1].TryGetValue(t2, out op)) return op; op = t1.GetMethod("op_Implicit", new Type[] { t2 }); using (new WriteLock(conv_ops_lock)) { try { conv_ops[t1].Add(t2, op); } catch (ArgumentException) { return conv_ops[t1][t2]; } } return op; } private static bool TryReadInt(Type inst_type, JsonReader reader, out int value) { value = 0; if (reader.Token != JsonToken.Int) return false; value = reader.ValueInt; return true; } private static bool TryReadLong(Type inst_type, JsonReader reader, out long value) { value = 0; if (reader.Token != JsonToken.Long) return false; value = reader.ValueLong; return true; } private static bool TryReadULong(Type inst_type, JsonReader reader, out ulong value) { value = 0; if (reader.Token != JsonToken.ULong) return false; value = reader.ValueULong; return true; } private static bool TryReadDouble(Type inst_type, JsonReader reader, out double value) { value = 0; if (reader.Token != JsonToken.Double) return false; value = reader.ValueDouble; return true; } private static bool TryReadBool(Type inst_type, JsonReader reader, out bool value) { value = false; if (reader.Token != JsonToken.Boolean) return false; value = reader.ValueBool; return true; } private static unsafe object ReadValue(Type inst_type, JsonReader reader, bool skipRead = false) { if (!skipRead) reader.Read(); if (reader.Token == JsonToken.ArrayEnd) return null; Type underlying_type = inst_type.IsGenericType && inst_type.GetGenericTypeDefinition() == nullable_type ? Nullable.GetUnderlyingType(inst_type) : null; Type value_type = underlying_type ?? inst_type; if (reader.Token == JsonToken.Null) { if (inst_type.IsClass || underlying_type != null) return null; throw new JsonException(String.Format( "Can't assign null to an instance of type {0}", inst_type)); } if (reader.Token == JsonToken.Double || reader.Token == JsonToken.Int || reader.Token == JsonToken.Long || reader.Token == JsonToken.ULong || reader.Token == JsonToken.String || reader.Token == JsonToken.Boolean) { object value = null; Type json_type = null;// reader.Value.GetType(); switch (reader.Token) { case JsonToken.Int: json_type = int_type; value = reader.ValueInt; break; case JsonToken.Long: json_type = long_type; value = reader.ValueLong; break; case JsonToken.ULong: json_type = ulong_type; value = reader.ValueULong; break; case JsonToken.Boolean: json_type = bool_type; value = reader.ValueBool; break; case JsonToken.Double: json_type = double_type; value = reader.ValueDouble; break; case JsonToken.String: json_type = string_type; value = GetStringInstance(reader.ValueString); break; } if (value_type.IsAssignableFrom(json_type)) return value; // If there's a custom importer that fits, use it if (custom_importers_table.TryGetValue(json_type, out var typeTable) && typeTable.TryGetValue(value_type, out var cutomImporter)) { return cutomImporter(value); } // Maybe there's a base importer that works if (base_importers_table.TryGetValue(json_type, out var importersTable) && importersTable.TryGetValue(value_type, out var importersImporter)) { return importersImporter(value); } // Maybe it's an enum if (value_type.IsEnum) { if (reader.Token == JsonToken.String) return Enum.Parse(value_type, value.ToString(), true); return Enum.ToObject(value_type, value); } // Try using an implicit conversion operator MethodInfo conv_op = GetConvOp(value_type, json_type); if (conv_op != null) return conv_op.Invoke(null, new object[] { value }); // No luck throw new JsonException($"Can't assign value '{value}' (type {json_type}) to type {inst_type}"); } object instance = null; if (reader.Token == JsonToken.ArrayStart) { //if (inst_type.FullName == "System.Object") if (inst_type == typeof(System.Object)) inst_type = typeof(object[]); ArrayMetadata t_data = AddArrayMetadata(inst_type); if (!t_data.IsArray && !t_data.IsList) throw new JsonException(String.Format( "Type {0} can't act as an array", inst_type)); IList list; Type elem_type; if (!t_data.IsArray) { //list = (IList)Activator.CreateInstance(inst_type); list = (IList)GetInstance(inst_type); elem_type = t_data.ElementType; } else { list = new System.Collections.Generic.List(); elem_type = inst_type.GetElementType(); } list.Clear(); while (true) { object item = ReadValue(elem_type, reader); if (item == null && reader.Token == JsonToken.ArrayEnd) break; list.Add(item); } if (t_data.IsArray) { int n = list.Count; instance = Array.CreateInstance(elem_type, n); for (int i = 0; i < n; i++) ((Array)instance).SetValue(list[i], i); } else instance = list; } else if (reader.Token == JsonToken.ObjectStart || reader.Token == JsonToken.PropertyName) { if (inst_type == typeof(System.Object)) value_type = inst_type = typeof(Dictionary); ObjectMetadata t_data = AddObjectMetadata(value_type); //instance = Activator.CreateInstance(value_type); instance = GetInstance(value_type); var fastSetter = instance as IFastSetter; while (true) { if (reader.Token != JsonToken.PropertyName) reader.Read(); if (reader.Token == JsonToken.ObjectEnd) break; if (fastSetter != null) { if (_tmpProperty == null || _tmpProperty.Length < reader.ValueString.Length) _tmpProperty = new char[reader.ValueString.Length]; reader.ValueString.CopyTo(_tmpProperty); Span prop = stackalloc char[reader.ValueString.Length]; _tmpProperty.AsSpan(0, reader.ValueString.Length).CopyTo(prop); reader.Read(); switch (reader.Token) { case JsonToken.Int: if (TryReadInt(inst_type, reader, out var value_int)) fastSetter.SetInteger(prop, value_int); break; case JsonToken.Long: if (TryReadLong(inst_type, reader, out var value_long)) fastSetter.SetInteger(prop, value_long); break; case JsonToken.ULong: if (TryReadULong(inst_type, reader, out var value_ulong)) { if (value_ulong > (ulong)long.MaxValue) throw new JsonException($"Can't call SetInteger for property '{new string(prop)}', because value_long({value_ulong}) is larger than long.MaxValue!"); fastSetter.SetInteger(prop, (long)value_ulong); } break; case JsonToken.Double: if (TryReadDouble(inst_type, reader, out var value_double)) fastSetter.SetDouble(prop, value_double); break; case JsonToken.Boolean: if (TryReadBool(inst_type, reader, out var value_bool)) fastSetter.SetBool(prop, value_bool); break; case JsonToken.ObjectStart: reader.Read(); goto default; default: if (t_data.Properties.TryGetValue(_tmpProperty.AsMemory(0, prop.Length), out var prop_meta_data)) { fastSetter.SetOther(prop, ReadValue(prop_meta_data.Type, reader, reader.Token != JsonToken.ObjectStart)); } else ReadSkip(reader); break; } } else if (!t_data.IsDictionary && t_data.Properties.TryGetValue(reader.ValueString, out var prop_data)) { try { if (prop_data.IsField) { ((FieldInfo)prop_data.Info).SetValue( instance, ReadValue(prop_data.Type, reader)); } else { PropertyInfo p_info = (PropertyInfo)prop_data.Info; if (p_info.CanWrite) p_info.SetValue( instance, ReadValue(prop_data.Type, reader), null); else ReadValue(prop_data.Type, reader); } } catch (JsonException ex) { throw new JsonException($"While parsing property '{new string(reader.ValueString.Span)}': {ex.Message}"); } } else { if (!t_data.IsDictionary) { if (!reader.SkipNonMembers) { throw new JsonException(String.Format( "The type {0} doesn't have the " + "property '{1}'", inst_type, new string(reader.ValueString.Span))); } else { ReadSkip(reader); continue; } } try { ((IDictionary)instance).Add( GetStringInstance(reader.ValueString), ReadValue( t_data.ElementType, reader)); } catch (JsonException ex) { throw new JsonException($"While parsing property '{new string(reader.ValueString.Span)}': {ex.Message}"); } } } } return instance; } private static IJsonWrapper ReadValue(WrapperFactory factory, JsonReader reader) { reader.Read(); if (reader.Token == JsonToken.ArrayEnd || reader.Token == JsonToken.Null) return null; IJsonWrapper instance = factory?.Invoke(); if (reader.Token == JsonToken.String) { instance?.SetString(new string(reader.ValueString.Span)); return instance; } if (reader.Token == JsonToken.Double) { instance?.SetDouble(reader.ValueDouble); return instance; } if (reader.Token == JsonToken.Int) { instance?.SetInt(reader.ValueInt); return instance; } if (reader.Token == JsonToken.Long) { instance?.SetLong(reader.ValueLong); return instance; } if (reader.Token == JsonToken.Boolean) { instance?.SetBoolean(reader.ValueBool); return instance; } if (reader.Token == JsonToken.ArrayStart) { instance?.SetJsonType(JsonType.Array); while (true) { IJsonWrapper item = ReadValue(factory, reader); if (item == null && reader.Token == JsonToken.ArrayEnd) break; if (instance != null) ((IList)instance).Add(item); } } else if (reader.Token == JsonToken.ObjectStart) { instance?.SetJsonType(JsonType.Object); while (true) { reader.Read(); if (reader.Token == JsonToken.ObjectEnd) break; string property = new string(reader.ValueString.Span); if (instance != null) ((IDictionary)instance)[property] = ReadValue(factory, reader); } } return instance; } private static void ReadSkip(JsonReader reader) { ToWrapper(null, reader); } private static void RegisterBaseExporters() { base_exporters_table[typeof(byte)] = delegate (object obj, JsonWriter writer) { writer.Write(Convert.ToInt32((byte)obj)); }; base_exporters_table[typeof(char)] = delegate (object obj, JsonWriter writer) { writer.Write(Convert.ToString((char)obj)); }; base_exporters_table[typeof(DateTime)] = delegate (object obj, JsonWriter writer) { writer.Write(Convert.ToString((DateTime)obj, datetime_format)); }; base_exporters_table[typeof(decimal)] = delegate (object obj, JsonWriter writer) { writer.Write((decimal)obj); }; base_exporters_table[typeof(sbyte)] = delegate (object obj, JsonWriter writer) { writer.Write(Convert.ToInt32((sbyte)obj)); }; base_exporters_table[typeof(short)] = delegate (object obj, JsonWriter writer) { writer.Write(Convert.ToInt32((short)obj)); }; base_exporters_table[typeof(ushort)] = delegate (object obj, JsonWriter writer) { writer.Write(Convert.ToInt32((ushort)obj)); }; base_exporters_table[typeof(uint)] = delegate (object obj, JsonWriter writer) { writer.Write(Convert.ToUInt64((uint)obj)); }; base_exporters_table[typeof(ulong)] = delegate (object obj, JsonWriter writer) { writer.Write((ulong)obj); }; base_exporters_table[typeof(DateTimeOffset)] = delegate (object obj, JsonWriter writer) { writer.Write(((DateTimeOffset)obj).ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz", datetime_format)); }; } private static void RegisterBaseImporters() { ImporterFunc importer; importer = delegate (object input) { return Convert.ToByte((int)input); }; RegisterImporter(base_importers_table, typeof(int), typeof(byte), importer); importer = delegate (object input) { return Convert.ToUInt64((int)input); }; RegisterImporter(base_importers_table, typeof(int), typeof(ulong), importer); importer = delegate (object input) { return Convert.ToInt64((int)input); }; RegisterImporter(base_importers_table, typeof(int), typeof(long), importer); importer = delegate (object input) { return Convert.ToSByte((int)input); }; RegisterImporter(base_importers_table, typeof(int), typeof(sbyte), importer); importer = delegate (object input) { return Convert.ToInt16((int)input); }; RegisterImporter(base_importers_table, typeof(int), typeof(short), importer); importer = delegate (object input) { return Convert.ToUInt16((int)input); }; RegisterImporter(base_importers_table, typeof(int), typeof(ushort), importer); importer = delegate (object input) { return Convert.ToUInt32((int)input); }; RegisterImporter(base_importers_table, typeof(int), typeof(uint), importer); importer = delegate (object input) { return Convert.ToSingle((int)input); }; RegisterImporter(base_importers_table, typeof(int), typeof(float), importer); importer = delegate (object input) { return Convert.ToDouble((int)input); }; RegisterImporter(base_importers_table, typeof(int), typeof(double), importer); importer = delegate (object input) { return Convert.ToDecimal((double)input); }; RegisterImporter(base_importers_table, typeof(double), typeof(decimal), importer); importer = delegate (object input) { return Convert.ToSingle((double)input); }; RegisterImporter(base_importers_table, typeof(double), typeof(float), importer); importer = delegate (object input) { return Convert.ToUInt32((long)input); }; RegisterImporter(base_importers_table, typeof(long), typeof(uint), importer); importer = delegate (object input) { return Convert.ToChar((string)input); }; RegisterImporter(base_importers_table, typeof(string), typeof(char), importer); importer = delegate (object input) { return Convert.ToDateTime((string)input, datetime_format); }; RegisterImporter(base_importers_table, typeof(string), typeof(DateTime), importer); importer = delegate (object input) { return DateTimeOffset.Parse((string)input, datetime_format); }; RegisterImporter(base_importers_table, typeof(string), typeof(DateTimeOffset), importer); } private static void RegisterImporter( IDictionary> table, Type json_type, Type value_type, ImporterFunc importer) { if (!table.TryGetValue(json_type, out var importerTable)) table.Add(json_type, importerTable = new Dictionary()); importerTable[value_type] = importer; } private static void WriteValue(object obj, JsonWriter writer, bool writer_is_private, int depth) { if (depth > max_nesting_depth) throw new JsonException( String.Format("Max allowed object depth reached while " + "trying to export from type {0}", obj.GetType())); if (obj == null) { writer.Write(null); return; } if (obj is IJsonWrapper) { if (writer_is_private) writer.TextWriter.Write(((IJsonWrapper)obj).ToJson()); else ((IJsonWrapper)obj).ToJson(writer); return; } if (obj is String) { writer.Write((string)obj); return; } if (obj is Double) { writer.Write((double)obj); return; } if (obj is Single) { writer.Write((float)obj); return; } if (obj is Int32) { writer.Write((int)obj); return; } if (obj is Boolean) { writer.Write((bool)obj); return; } if (obj is Int64) { writer.Write((long)obj); return; } if (obj is Array) { writer.WriteArrayStart(); foreach (object elem in (Array)obj) WriteValue(elem, writer, writer_is_private, depth + 1); writer.WriteArrayEnd(); return; } if (obj is IList) { writer.WriteArrayStart(); foreach (object elem in (IList)obj) WriteValue(elem, writer, writer_is_private, depth + 1); writer.WriteArrayEnd(); return; } var iDictionary = obj as IDictionary; if (iDictionary != null) { writer.WriteObjectStart(); foreach (DictionaryEntry entity in iDictionary) { var propertyName = entity.Key as string ?? Convert.ToString(entity.Key, CultureInfo.InvariantCulture); writer.WritePropertyName(propertyName); WriteValue(entity.Value, writer, writer_is_private, depth + 1); } writer.WriteObjectEnd(); return; } Type obj_type = obj.GetType(); // See if there's a custom exporter for the object if (custom_exporters_table.TryGetValue(obj_type, out var customExporter)) { customExporter(obj, writer); return; } // If not, maybe there's a base exporter if (base_exporters_table.TryGetValue(obj_type, out var baseExporter)) { baseExporter(obj, writer); return; } // Last option, let's see if it's an enum if (obj is Enum) { //Type e_type = Enum.GetUnderlyingType (obj_type); try { /*if (e_type == long_type || e_type == typeof(uint) || e_type == ulong_type) writer.Write((ulong)obj); else if (e_type == typeof(byte)) writer.Write((byte)obj); else writer.Write((int)obj);*/ writer.Write(Convert.ToInt32(obj)); } catch //(InvalidCastException ex) { throw new InvalidCastException($"Failed to cast {obj_type.Name}.'{obj}' of type '{Enum.GetUnderlyingType(obj_type).Name}'!"); } return; } // Okay, so it looks like the input should be exported as an // object List props = AddTypeProperties(obj_type); writer.WriteObjectStart(); foreach (PropertyMetadata p_data in props) { if (p_data.IsField) { writer.WritePropertyName(p_data.Info.Name); WriteValue(((FieldInfo)p_data.Info).GetValue(obj), writer, writer_is_private, depth + 1); } else { PropertyInfo p_info = (PropertyInfo)p_data.Info; if (p_info.CanRead) { writer.WritePropertyName(p_data.Info.Name); WriteValue(p_info.GetValue(obj, null), writer, writer_is_private, depth + 1); } } } writer.WriteObjectEnd(); } #endregion public static string ToJson(object obj) { static_writer ??= new JsonWriter(); static_writer.Reset(); WriteValue(obj, static_writer, true, 0); return static_writer.ToString(); } public static void ToJson(object obj, JsonWriter writer) { WriteValue(obj, writer, false, 0); } public static JsonData ToObject(JsonReader reader) { return (JsonData)ToWrapper( delegate { return new JsonData(); }, reader); } public static object ToObject(Type toType, JsonReader reader) { return ReadValue(toType, reader); } public static JsonData ToObject(TextReader reader) { JsonReader json_reader = new JsonReader(reader); return (JsonData)ToWrapper( delegate { return new JsonData(); }, json_reader); } public static JsonData ToObject(string json) { return (JsonData)ToWrapper( delegate { return new JsonData(); }, json); } public static T ToObject(JsonReader reader) { return (T)ReadValue(typeof(T), reader); } public static T ToObject(TextReader reader) { JsonReader json_reader = new JsonReader(reader); return (T)ReadValue(typeof(T), json_reader); } public static T ToObject(string json) { JsonReader reader = null; if (_readerPool.TryPop(out reader)) reader.SetJson(json); else reader = new JsonReader(json, true); try { return (T)ReadValue(typeof(T), reader); } finally { _readerPool.Push(reader); } } public static object ToObject(Type toType, string json) { JsonReader reader = new JsonReader(json); return ReadValue(toType, reader); } public static IJsonWrapper ToWrapper(WrapperFactory factory, JsonReader reader) { return ReadValue(factory, reader); } public static IJsonWrapper ToWrapper(WrapperFactory factory, string json) { JsonReader reader = new JsonReader(json); return ReadValue(factory, reader); } public static void RegisterExporter(ExporterFunc exporter) { ExporterFunc exporter_wrapper = delegate (object obj, JsonWriter writer) { exporter((T)obj, writer); }; custom_exporters_table[typeof(T)] = exporter_wrapper; } public static void RegisterImporter( ImporterFunc importer) { ImporterFunc importer_wrapper = delegate (object input) { return importer((TJson)input); }; RegisterImporter(custom_importers_table, typeof(TJson), typeof(TValue), importer_wrapper); } private static System.Func _instanceProvider; public static void RegisterInstanceProvider(System.Func provider) { _instanceProvider = provider; } public static void RegisterStringCache(string str) { _stringInstanceCache.TryAdd(str.AsMemory(), str); } public static string GetStringInstance(ReadOnlyMemory chars) { if (_stringInstanceCache.TryGetValue(chars, out var value)) return value; return new string(chars.Span); } public static object GetInstance(Type type) { var provider = _instanceProvider; object result = null; if (provider != null) result = provider(type); if (result == null) result = Activator.CreateInstance(type); return result; } public static void UnregisterExporters() { custom_exporters_table.Clear(); } public static void UnregisterImporters() { custom_importers_table.Clear(); } } }