Coder Social home page Coder Social logo

utf8json's Introduction

THIS PROJECT IS ARCHIVED, USE COMMUNITY FORK INSTEAD.

Utf8Json - Fast JSON Serializer for C#

Join the chat at https://gitter.im/neuecc/Utf8Json Releases

Definitely Fastest and Zero Allocation JSON Serializer for C#(.NET, .NET Core, Unity and Xamarin), this serializer write/read directly to UTF8 binary so boostup performance. And I adopt the same architecture as the fastest binary serializer, MessagePack for C# that I've developed.

image

This benchmark is convert object to UTF8 and UTF8 to object benchmark. It is not to string(.NET UTF16), so Jil, NetJSON and Json.NET contains additional UTF8.GetBytes/UTF8.GetString call. Definitely means does not exists encoding/decoding cost. Benchmark code is in sandbox/PerfBenchmark by BenchmarkDotNet.

I've tested more benchmark - Benchmark of Jil vs Utf8Json for test many dataset patterns(borrwed from Jil Benchmark) and three input/output compare(Object <-> byte[](Utf8), Object <-> Stream(Utf8) and Object <-> String(UTF16)). If target is UTF8(both byte[] and Stream), Utf8Json wins and memory allocated is extremely small.

Utf8Json does not beat MessagePack for C#(binary), but shows a similar memory consumption(there is no additional memory allocation) and achieves higher performance than other JSON serializers.

The crucial difference is that read and write directly to UTF8 binaries means that there is no overhead. Normaly serialization requires serialize to Stream or byte[], it requires additional UTF8.GetBytes cost or StreamReader/Writer overhead(it is very slow!).

TargetClass obj1;

// Object to UTF8 byte[]
[Benchmark]
public byte[] Utf8JsonSerializer()
{
    return Utf8Json.JsonSerializer.Serialize(obj1, jsonresolver);
}

// Object to String to UTF8 byte[]
[Benchmark]
public byte[] Jil()
{
    return utf8.GetBytes(global::Jil.JSON.Serialize(obj1));
}

// Object to Stream with StreamWriter
[Benchmark]
public void JilTextWriter()
{
    using (var ms = new MemoryStream())
    using (var sw = new StreamWriter(ms, utf8))
    {
        global::Jil.JSON.Serialize(obj1, sw);
    }
}

For example, the OutputFormatter of ASP.NET Core needs to write to Body(Stream), but using Jil's TextWriter overload is slow. (This not means Jil is slow, for example StreamWriter allocate many memory(char[1024] and byte[3075]) on constructor (streamwriter.cs#L203-L204) and other slow features unfortunately).

// ASP.NET Core, OutputFormatter
public class JsonOutputFormatter : IOutputFormatter //, IApiResponseTypeMetadataProvider
{
    const string ContentType = "application/json";
    static readonly string[] SupportedContentTypes = new[] { ContentType };

    public Task WriteAsync(OutputFormatterWriteContext context)
    {
        context.HttpContext.Response.ContentType = ContentType;

        // Jil, normaly JSON Serializer requires serialize to Stream or byte[].
        using (var writer = new StreamWriter(context.HttpContext.Response.Body))
        {
            Jil.JSON.Serialize(context.Object, writer, _options);
            writer.Flush();
            return Task.CompletedTask;
        }

        // Utf8Json
        // Utf8Json.JsonSerializer.NonGeneric.Serialize(context.ObjectType, context.HttpContext.Response.Body, context.Object, resolver);
    }
}

The approach of directly write/read from JSON binary is similar to corefxlab/System.Text.Json and corefxlab/System.Text.Formatting. But it is not yet finished and not be general serializer.

Corefxlab has UTF8String and C# discussing UTF8String Constants but maybe it is far future.

Install and QuickStart

The library provides in NuGet except for Unity. Standard library availables for .NET Framework 4.5 and .NET Standard 2.0.

Install-Package Utf8Json

And official Extension Packages for support other library(ImmutableCollection) or binding for framework(ASP.NET Core MVC).

Install-Package Utf8Json.ImmutableCollection
Install-Package Utf8Json.UnityShims
Install-Package Utf8Json.AspNetCoreMvcFormatter

NuGet page links - Utf8Json, Utf8Json.ImmutableCollection, Utf8Json.UnityShims, Utf8Json.AspNetCoreMvcFormatter

for Unity, you can download from releases page. There providing .unitypackage. Unity support details, see Unity section.

You can find third-party extension package like Utf8Json.FSharpExtensions for F# types, NServiceBus.Utf8Json or others.

QuickStart, you can call Utf8Json.JsonSerializer.Serialize/Deserialize.

var p = new Person { Age = 99, Name = "foobar" };

// Object -> byte[] (UTF8)
byte[] result = JsonSerializer.Serialize(p);

// byte[] -> Object
var p2 = JsonSerializer.Deserialize<Person>(result);

// Object -> String
var json = JsonSerializer.ToJsonString(p2);

// Write to Stream
JsonSerializer.Serialize(stream, p2);

In default, you can serialize all public members. You can customize serialize to private, exclude null, change DateTime format(default is ISO8601), enum handling, etc. see the Resolver section.

Performance of Serialize

This image is what code is generated when object serializing.

image

// Disassemble generated serializer code.
public sealed class PersonFormatter : IJsonFormatter<Person>
{
    private readonly byte[][] stringByteKeys;
    
    public PersonFormatter()
    {
        // pre-encoded escaped string byte with "{", ":" and ",".
        this.stringByteKeys = new byte[][]
        {
            JsonWriter.GetEncodedPropertyNameWithBeginObject("Age"), // {\"Age\":
            JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator("Name") // ,\"Name\":
        };
    }

    public sealed Serialize(ref JsonWriter writer, Person person, IJsonFormatterResolver jsonFormatterResolver)
    {
        if (person == null) { writer.WriteNull(); return; }

        // WriteRawX is optimize byte->byte copy when we know src size.
        UnsafeMemory64.WriteRaw7(ref writer, this.stringByteKeys[0]);
        writer.WriteInt32(person.Age); // itoa write directly to avoid ToString + UTF8 encode
        UnsafeMemory64.WriteRaw8(ref writer, this.stringByteKeys[1]);
        writer.WriteString(person.Name);

        writer.WriteEndObject();
    }

    // public unsafe Person Deserialize(ref JsonReader reader, IJsonFormatterResolver jsonFormatterResolver)
}

Object to JSON's main serialization cost is write property name. Utf8Json create cache at first and after that only do memory copy. Optimize part1, concatenate "{", ":" and "." to cached propertyname. Optimize part2, use optimized custom memory copy method(see: UnsafeMemory.cs). Normally memory copy is used Buffer.BlockCopy but it has some overhead when target binary is small enough, releated to dotnet/coreclr - issue #9786 Optimize Buffer.MemoryCopy and dotnet/coreclr - Add a fast path for byte[] to Buffer.BlockCopy #3118. Utf8Json don't use Buffer.BlockCopy and generates length specialized copy code that can reduce branch cost.

Number conversion is often high cost. If target encoding is UTF8 only, we can use itoa algorithm so avoid int.ToString and UTF8 encode cost. Especialy double-conversion, Utf8Json ported google/double-conversion algorithm, it is fast dtoa and atod works.

Other optimize techniques.

  • High-level API uses internal memory pool, don't allocate working memory under 64K
  • Struct JsonWriter does not allocate any more and write underlying byte[] directly, don't use TextWriter
  • Avoid boxing all codes, all platforms(include Unity/IL2CPP)
  • Heavyly tuned dynamic IL code generation, it generates per option so reduce option check: see:DynamicObjectResolver.cs
  • Call Primitive API directly when IL code generation knows target is primitive
  • Getting cached generated formatter on static generic field(don't use dictionary-cache because lookup is overhead)
  • Don't use IEnumerable<T> abstraction on iterate collection, specialized each collection types, see:CollectionFormatter.cs
  • Uses optimized type key dictionary for non-generic methods, see: ThreadsafeTypeKeyHashTable.cs

Performance of Deserialize

When deserializing, requires property name to target member name matching. Utf8Json avoid string key decode for matching, generate automata based IL inlining code.

image

use raw byte[] slice and try to match each ulong type (per 8 character, if it is not enough, pad with 0).

// Disassemble generated serializer code.
public sealed class PersonFormatter : IJsonFormatter<Person>
{
    // public sealed Serialize(ref JsonWriter writer, Person person, IJsonFormatterResolver jsonFormatterResolver)

    public unsafe Person Deserialize(ref JsonReader reader, IJsonFormatterResolver jsonFormatterResolver)
    {
        if (reader.ReadIsNull()) return null;
        
        reader.ReadIsBeginObjectWithVerify(); // "{"
        
        byte[] bufferUnsafe = reader.GetBufferUnsafe();
        int age;
        string name;
        fixed (byte* ptr2 = &bufferUnsafe[0])
        {
            int num;
            while (!reader.ReadIsEndObjectWithSkipValueSeparator(ref num)) // "}" or ","
            {
                // don't decode string, get raw slice
                ArraySegment<byte> arraySegment = reader.ReadPropertyNameSegmentRaw();
                byte* ptr3 = ptr2 + arraySegment.Offset;
                int count = arraySegment.Count;
                if (count != 0)
                {
                    // match automata per long
                    ulong key = AutomataKeyGen.GetKey(ref ptr3, ref count);
                    if (count == 0)
                    {
                        if (key == 6645569uL)
                        {
                            age = reader.ReadInt32(); // atoi read directly to avoid GetString + int.Parse
                            continue;
                        }
                        if (key == 1701667150uL)
                        {
                            name = reader.ReadString();
                            continue;
                        }
                    }
                }
                reader.ReadNextBlock();
            }
        }

        return new PersonSample
        {
            Age = age,
            Name = name
        };
    }
}

Of course number conversion(decode to string -> try parse) is high cost. Utf8Json directly convert byte[] to number by atoi/atod algorithm.

Built-in support types

These types can serialize by default.

Primitives(int, string, etc...), Enum, Nullable<>, TimeSpan, DateTime, DateTimeOffset, Guid, Uri, Version, StringBuilder, BitArray, Type, ArraySegment<>, BigInteger, Complext, ExpandoObject , Task, Array[], Array[,], Array[,,], Array[,,,], KeyValuePair<,>, Tuple<,...>, ValueTuple<,...>, List<>, LinkedList<>, Queue<>, Stack<>, HashSet<>, ReadOnlyCollection<>, IList<>, ICollection<>, IEnumerable<>, Dictionary<,>, IDictionary<,>, SortedDictionary<,>, SortedList<,>, ILookup<,>, IGrouping<,>, ObservableCollection<>, ReadOnlyOnservableCollection<>, IReadOnlyList<>, IReadOnlyCollection<>, ISet<>, ConcurrentBag<>, ConcurrentQueue<>, ConcurrentStack<>, ReadOnlyDictionary<,>, IReadOnlyDictionary<,>, ConcurrentDictionary<,>, Lazy<>, Task<>, custom inherited ICollection<> or IDictionary<,> with paramterless constructor, IEnumerable, ICollection, IList, IDictionary and custom inherited ICollection or IDictionary with paramterless constructor(includes ArrayList and Hashtable), Exception and inherited exception types(serialize only) and your own class or struct(includes anonymous type).

Utf8Json has sufficient extensiblity. You can add custom type support and has some official/third-party extension package. for example ImmutableCollections(ImmutableList<>, etc), Utf8Json.FSharpExtensions(FSharpOption, FSharpList, etc...). Please see extensions section.

Object Serialization

Utf8Json can serialze your own public Class or Struct. In default, serializer search all public instance member(field or property) and uses there member name as json property name. If you want to avoid serialization target, you can use [IgnoreDataMember] attribute of System.Runtime.Serialization to target member. If you want to change property name, you can use [DataMember(Name = string)] attribute of System.Runtime.Serialization.

// JsonSerializer.Serialize(new FooBar { FooProperty = 99, BarProperty = "BAR" });
// Result : {"foo":99}
public class FooBar
{
    [DataMember(Name = "foo")]
    public int FooProperty { get; set; }

    [IgnoreDataMember]
    public string BarProperty { get; set; }
}

Utf8Json has other option, allows private/internal member serialization, convert property name to camelCalse/snake_case, if value is null does not create property. Or you can use a different DateTime format(default is ISO8601). The details, please read Resolver section. Here is sample.

// default serializer change to allow private/exclude null/snake_case serializer.
JsonSerializer.SetDefaultResolver(StandardResolver.AllowPrivateExcludeNullSnakeCase);

var json = JsonSerializer.ToJsonString(new Person { Age = 23, FirstName = null, LastName = "Foo" });

// {"age":23,"last_name":"Foo"}
Console.WriteLine(json);

Serialize ImmutableObject(SerializationConstructor)

Utf8Json can deserialize immutable object like this.

public struct CustomPoint
{
    public readonly int X;
    public readonly int Y;

    public CustomPoint(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }
}

Utf8Json choose constructor with the most matched argument by name(ignore case).

MessagePack for C# choose least matched argument, please be aware of the opposite. This is design miss of MessagePack for C#.

If can not match automatically, you can specify to use constructor manually by [SerializationConstructorAttribute].

public class CustomPoint
{
    public readonly int X;
    public readonly int Y;

    public CustomPoint(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }

    // used this constructor.
    [SerializationConstructor]
    public CustomPoint(int x)
    {
        this.X = x;
    }
}

ShouldSerializeXXX pattern

UtfJson supports ShouldSerialize feature of Json.NET. If defined public bool ShouldMemberName() method, call method before serialize member value and if false does not output member.

public class MyPerson
{
    public string Name { get; set; }
    public string[] Addresses { get; set; }

    // ShouldSerialize*membername**
    // method must be `public` and return `bool` and parameter less.
    public bool ShouldSerializeAddresses()
    {
        if (Addresses != null && Addresses.Length != 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

--

// {"Name":"foo"}
JsonSerializer.ToJsonString(new MyPerson { Name = "foo", Addresses = new string[0] });
        
// {"Name":"bar","Addresses":["tokyo","kyoto"]}
JsonSerializer.ToJsonString(new MyPerson { Name = "bar", Addresses = new[] { "tokyo", "kyoto" } });

Dynamic Deserialization

If use JsonSerializer.Deserialize or JsonSerializer.Deserialize, convert json to bool, double, string, IDictionary<string, object>, List<object>.

// dynamic json deserialize
var json = JsonSerializer.Deserialize<dynamic>(@"{""foo"":""json"",""bar"":100,""nest"":{""foobar"":true}}");

var r1 = json["foo"]; // "json" - dynamic(string)
var r2 = json["bar"]; // 100 - dynamic(double), it can cast to int or other number.
var r3 = json["nest"]["foobar"]; // true

If target is object, you access by string indexer.

JSON Comments

JSON Comments is invalid JSON Format but used widely(for example, VSCode - settings.json) and also supports JSON.NET. Utf8Json suports both single-line comment and multi-line comment.

{
    // allow single line comment
    "foo": true, // trailing
    /*
      allow
      multi
      line
      comment
    */
    "bar": 999 /* trailing */
}

Encoding

Utf8Json only supports UTF-8 but it is valid on latest JSON Spec - RFC8259 The JavaScript Object Notation (JSON) Data Interchange Format, DECEMBER 2017.

It mentions about encoding.

8.1. Character Encoding JSON text exchanged between systems that are not part of a closed ecosystem MUST be encoded using UTF-8 [RFC3629].

Previous specifications of JSON have not required the use of UTF-8 when transmitting JSON text. However, the vast majority of JSON based software implementations have chosen to use the UTF-8 encoding, to the extent that it is the only encoding that achieves interoperability.

Which serializer should be used

The performance of binary(protobuf, msgpack, avro, etc...) vs text(json, xml, yaml, etc...) depends on the implementation. However, binary has advantage basically. Utf8Json write directly to byte[] it is close to the binary serializer. But especialy double is still slower than binary write(Utf8Json uses google/double-conversion algorithm, it is good but there are many processes, it can not be the fastest), write string requires escape and large payload must pay copy cost.

I recommend use MessagePack for C# for general use serializer, C# to C#, C# to NoSQL, Save to File, communicate internal cross platform(multi-language), etc. MessagePack for C# has many options(Union ,Typeless, Compression) and definitely fastest.

But JSON is still better on web, for public Web API, send for JavaScript and easy to integrate between multi-language communication. For example, use Utf8Json for Web API formatter and use MessagePack for C# for Redis. It is perfect.

For that reason Utf8Json is focusing performance and cross-platform compatibility. I don't implement original format(like Union, Typeless, Cyclic-Reference) if you want to use it should be use binary serializer. But customizability for serialize/deserialize JSON is important for cross-platform communication. IJsonFormatterResolver can serialize/deserialize all patterns and you can create own pattern.

High-Level API(JsonSerializer)

JsonSerializer is the entry point of Utf8Json. Its static methods are main API of Utf8Json.

API Description
DefaultResolver FormatterResolver that used resolver less overloads. If does not set it, used StandardResolver.Default.
SetDefaultResolver Set default resolver of JsonSerializer APIs.
Serialize<T> Convert object to byte[] or write to stream. There has IJsonFormatterResolver overload, used specified resolver.
SerializeUnsafe<T> Same as Serialize<T> but return ArraySegement<byte>. The result of ArraySegment is contains internal buffer pool, it can not share across thread and can not hold, so use quickly.
SerializeAsync<T> Convert object to byte[] and write to stream async.
ToJsonString<T> Convert object to string.
Deserialize<T> Convert byte[] or ArraySegment<byte> or stream to object. There has IFormatterResolver overload, used specified resolver.
DeserializeAsync<T> Convert stream(read async to buffer byte[]) to object.
PrettyPrint Output indented json string.
PrettyPrintByteArray Output indented json string(UTF8 byte[]).
NonGeneric.* NonGeneric APIs of Serialize/Deserialize. There accept type parameter at first argument. This API is bit slower than generic API but useful for framework integration such as ASP.NET formatter.

Utf8Json operates at the byte[] level, so Deserialize<T>(Stream) read to end at first, it is not truly streaming deserialize.

High-Level API uses memory pool internaly to avoid unnecessary memory allocation. If result size is under 64K, allocates GC memory only for the return bytes.

Low-Level API(IJsonFormatter)

IJsonFormatter is serializer by each type. For example Int32Formatter : IJsonFormatter<Int32> represents Int32 JSON serializer.

public interface IJsonFormatter<T> : IJsonFormatter
{
    void Serialize(ref JsonWriter writer, T value, IJsonFormatterResolver formatterResolver);
    T Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver);
}

Many builtin formatters exists under Utf8Json.Formatters. You can get sub type serializer by formatterResolver.GetFormatter<T>. Here is sample of write own formatter.

// serialize fileinfo as string fullpath.
public class FileInfoFormatter<T> : IJsonFormatter<FileInfo>
{
    public void Serialize(ref JsonWriter writer, FileInfo value, IJsonFormatterResolver formatterResolver)
    {
        if (value == null) { writer.WriteNull(); return; }

        // if target type is primitive, you can also use writer.Write***.
        formatterResolver.GetFormatterWithVerify<string>().Serialize(ref writer, value.FullName, formatterResolver);
    }

    public FileInfo Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
    {
        if (reader.ReadIsNull()) return null;

        // if target type is primitive, you can also use reader.Read***.
        var path = formatterResolver.GetFormatterWithVerify<string>().Deserialize(ref reader, formatterResolver);
        return new FileInfo(path);
    }
}

Created formatter needs to register to IFormatterResolver. Please see Resolver section.

You can see many other samples from builtin formatters.

If target type requires support dictionary key, you need to implements IObjectPropertyNameFormatter<T>, too.

Primitive API(JsonReader/JsonWriter)

JsonReader and JsonWriter is most low-level API. It is mutable struct so it must pass by ref and must does not store to field. C# 7.2 supports ref-like types(see: csharp-7.2/span-safety.md) and readonly-ref(see: csharp-7.2/Readonly references) but not yet implements in C#, be careful to use.

JsonReader and JsonWriter is too primitive(performance reason), slightly odd. Internal state manages only int offset. You should manage other state(in array, in object...) manualy in outer.

JsonReader

Method Description
AdvanceOffset Advance offset manually.
SkipWhiteSpace Skip whitespace.
ReadNext Skip JSON token.
ReadNextBlock Skip JSON token with sub structures(array/object). This is useful for create deserializer.
ReadNextBlockSegment Read next block and returns there array-segment.
ReadIsNull If is null return true.
ReadIsBeginArray If is '[' return true.
ReadIsBeginArrayWithVerify If is '[' return true.
ReadIsEndArray If is ']' return true.
ReadIsEndArrayWithVerify If is not ']' throws exception.
ReadIsEndArrayWithSkipValueSeparator check reached ']' or advance ',' when (ref int count) is not zero.
ReadIsInArray Convinient pattern of ReadIsBeginArrayWithVerify + while(!ReadIsEndArrayWithSkipValueSeparator)
ReadIsBeginObject If is '{' return true.
ReadIsBeginObjectWithVerify If is not '{' throws exception.
ReadIsEndObject If is '}' return true.
ReadIsEndObjectWithVerify If is not '}' throws exception.
ReadIsEndObjectWithSkipValueSeparator check reached '}' or advance ',' when (ref int count) is not zero.
ReadIsInObject Convinient pattern of ReadIsBeginObjectWithVerify + while(!ReadIsEndObjectWithSkipValueSeparator).
ReadIsValueSeparator If is ',' return true.
ReadIsValueSeparatorWithVerify If is not ',' throws exception.
ReadIsNameSeparator If is ':' return true.
ReadIsNameSeparatorWithVerify If is not ':' throws exception.
ReadString ReadString, unescaped.
ReadStringSegmentUnsafe ReadString block but does not decode string. Return buffer is in internal buffer pool, be careful to use.
ReadNumberSegment Read number as buffer slice.
ReadPropertyName ReadString + ReadIsNameSeparatorWithVerify.
ReadPropertyNameSegmentRaw Get raw string-span(do not unescape) + ReadIsNameSeparatorWithVerify.
ReadBoolean ReadBoolean.
ReadSByte atoi.
ReadInt16 atoi.
ReadInt32 atoi.
ReadInt64 atoi.
ReadByte atoi.
ReadUInt16 atoi.
ReadUInt32 atoi.
ReadUInt64 atoi.
ReadUInt16 atoi.
ReadSingle atod.
ReadDouble atod.
GetBufferUnsafe return underlying buffer.
GetCurrentOffsetUnsafe return underlying offset.
GetCurrentJsonToken Get current token(skip whitespace), do not advance.

Read*** methods advance next token when called. JsonReader reads utf8 byte[] to primitive directly.

How to use, see the List formatter.

// JsonReader is struct, always pass ref and do not set local variable.
public List<T> Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
{
    // check current state is null. when null, advanced offset. if not, do not.
    if (reader.ReadIsNull()) return null;

    var formatter = formatterResolver.GetFormatterWithVerify<T>();
    var list = new List<T>();

    var count = 0; // managing array-count state in outer(this is count, not index(index is always count - 1)

    // loop helper for Array or Object, you can use ReadIsInArray/ReadIsInObject.
    while (reader.ReadIsInArray(ref count)) // read '[' or ',' when until reached ']'
    {
        list.Add(formatter.Deserialize(ref reader, formatterResolver));
    }

    return list;
}

JsonWriter

Method Description
static GetEncodedPropertyName Get JSON Encoded byte[].
static GetEncodedPropertyNameWithPrefixValueSeparator Get JSON Encoded byte[] with ',' on prefix.
static GetEncodedPropertyNameWithBeginObject Get JSON Encoded byte[] with '{' on prefix.
static GetEncodedPropertyNameWithoutQuotation Get JSON Encoded byte[] without pre/post '"'.
CurrentOffset Get current offset.
AdvanceOffset Advance offset manually.
GetBuffer Get current buffer.
ToUtf8ByteArray Finish current buffer to byte[].
ToString Finish current buffer to json stirng.
EnsureCapacity Ensure inner buffer capacity.
WriteRaw Write byte/byte[] directly.
WriteRawUnsafe WriteRaw but don't check and ensure capacity.
WriteBeginArray Write '['.
WriteEndArray Write ']'.
WriteBeginObject Write '{'.
WriteEndObject Write '}'.
WriteValueSeparator Write ','.
WriteNameSeparator Write ':'.
WritePropertyName WriteString + WriteNameSeparator.
WriteQuotation Write '"'.
WriteNull Write 'null'.
WriteBoolean Write 'true' or 'false'.
WriteTrue Write 'true'.
WriteFalse Write 'false'.
WriteSByte itoa.
WriteInt16 itoa.
WriteInt32 itoa.
WriteInt64 itoa.
WriteByte itoa.
WriteUInt16 itoa.
WriteUInt32 itoa.
WriteUInt64 itoa.
WriteUInt16 itoa.
WriteSingle dtoa.
WriteDouble dtoa.

GetBuffer, ToUtf8ByteArray, ToString get the wrote result. JsonWriter writes primitive to utf8 bytes directly.

How to use, see the List formatter.

// JsonWriter is struct, always pass ref and do not set local variable.
public void Serialize(ref JsonWriter writer, List<T> value, IJsonFormatterResolver formatterResolver)
{
    if (value == null) { writer.WriteNull(); return; }

    var formatter = formatterResolver.GetFormatterWithVerify<T>();

    writer.WriteBeginArray(); // "["
    if (value.Count != 0)
    {
        formatter.Serialize(ref writer, value[0], formatterResolver);
    }
    for (int i = 1; i < value.Count; i++)
    {
        writer.WriteValueSeparator(); // write "," manually
        formatter.Serialize(ref writer, value[i], formatterResolver);
    }
    writer.WriteEndArray(); // "]"
}

How to write complex type formatter, you can refer KeyValuePairFormatter, it caches string table for serialize and automata dictionary for deserialize in outer helper class. How to add the custom formatter to custom resolver, you can see DynamicGenericResolver for generic formatter, BuiltinResolver for nongeneric formatter.

Resolver

IJsonFormatterResolver is storage of typed serializers. Serializer api accepts resolver and can customize serialization.

Resovler Name Description
BuiltinResolver Builtin primitive and standard classes resolver. It includes primitive(int, bool, string...) and there nullable, array and list. and some extra builtin types(Guid, Uri, BigInteger, etc...).
DynamicGenericResolver Resolver of generic type(Tuple<>, List<>, Dictionary<,>, Array, etc). It uses reflection call for resolve generic argument at first time.
AttributeFormatterResolver Get formatter from [JsonFormatter] attribute.
EnumResolver EnumResolver.Default serialize as name, EnumResolver.UnderlyingValue serialize as underlying value. Deserialize, can be both.
StandardResolver Composited resolver. It resolves in the following order object fallback -> (builtin -> enum -> dynamic generic -> attribute -> dynamic object). StandardResolver.Default is default resolver of JsonSerialzier and has many option resolvers, see below.
CompositeResolver Singleton custom composite resolver.

StandardResolver has 12 option resolvers it combinate

  • AllowPrivate = true/false
  • ExcludeNull = true/false
  • NameMutate = Original/CamelCase/SnakeCase.

for example StandardResolver.SnakeCase, StandardResolver.ExcludeNullCamelCase, StandardResolver.AllowPrivateExcludeNullSnakeCase. StandardResolver.Default is AllowPrivate:False, ExcludeNull:False, NameMutate:Original.

If AllowPrivate = true and does not match any constructor, deserializer uses FormatterServices.GetUninitializedObject to create new instance so AllowPrivate = true can deserialize all concrete types.

Assemble the resolver's priority is the only configuration point of Utf8Json. It is too simple but well works. In most cases, it is sufficient to have one custom resolver globally. CompositeResolver will be its helper. It is also necessary to use extension resolver like Utf8Json.ImmutableCollection that add support for for System.Collections.Immutable library. It adds ImmutableArray<>, ImmutableList<>, ImmutableDictionary<,>, ImmutableHashSet<>, ImmutableSortedDictionary<,>, ImmutableSortedSet<>, ImmutableQueue<>, ImmutableStack<>, IImmutableList<>, IImmutableDictionary<,>, IImmutableQueue<>, IImmutableSet<>, IImmutableStack<> serialization support.

// use global-singleton CompositeResolver.
// This method initialize CompositeResolver and set to default JsonSerializer
CompositeResolver.RegisterAndSetAsDefault(new IJsonFormatter[] {
    // add custome formatters, use other DateTime format.
    // if target type is struct, requires add nullable formatter too(use NullableXxxFormatter or StaticNullableFormatter(innerFormatter))
    new DateTimeFormatter("yyyy-MM-dd HH:mm:ss"),
    new NullableDateTimeFormatter("yyyy-MM-dd HH:mm:ss")
}, new[] {
    // resolver custom types first
    ImmutableCollectionResolver.Instance,
    EnumResolver.UnderlyingValue,

    // finaly choose standard resolver
    StandardResolver.AllowPrivateExcludeNullSnakeCase
});
// select resolver per invoke.
JsonSerializer.Serialize(value, StandardResolver.Default);
JsonSerializer.Serialize(value, StandardResolver.SnakeCase);
JsonSerializer.Serialize(value, CompositeResolver.Instance);

You can also build own custom composite resolver.

// create custom composite resolver per project is recommended way.
// let's try to copy and paste:)
public class ProjectDefaultResolver : IJsonFormatterResolver
{
    public static IJsonFormatterResolver Instance = new ProjectDefaultResolver();

    // configure your resolver and formatters.
    static IJsonFormatter[] formatters = new IJsonFormatter[]{
        new DateTimeFormatter("yyyy-MM-dd HH:mm:ss"),
        new NullableDateTimeFormatter("yyyy-MM-dd HH:mm:ss")
    };

    static readonly IJsonFormatterResolver[] resolvers = new[]
    {
        ImmutableCollectionResolver.Instance,
        EnumResolver.UnderlyingValue,
        StandardResolver.AllowPrivateExcludeNullSnakeCase
    };

    ProjectDefaultResolver()
    {
    }

    public IJsonFormatter<T> GetFormatter<T>()
    {
        return FormatterCache<T>.formatter;
    }

    static class FormatterCache<T>
    {
        public static readonly IJsonFormatter<T> formatter;

        static FormatterCache()
        {
            foreach (var item in formatters)
            {
                foreach (var implInterface in item.GetType().GetTypeInfo().ImplementedInterfaces)
                {
                    var ti = implInterface.GetTypeInfo();
                    if (ti.IsGenericType && ti.GenericTypeArguments[0] == typeof(T))
                    {
                        formatter = (IJsonFormatter<T>)item;
                        return;
                    }
                }
            }

            foreach (var item in resolvers)
            {
                var f = item.GetFormatter<T>();
                if (f != null)
                {
                    formatter = f;
                    return;
                }
            }
        }
    }
}

Or you can create and store dynamic CompositeResolver.

public static MyOwnProjectResolver
{
    // CompositeResolver.Create can create dynamic composite resolver.
    // It can `not` garbage collect and create is slightly high cost.
    // so you should store to static field.
    public static readonly IJsonFormatterResolver Instance = CompositeResolver.Create(
        /* IJsonFormatter[] */,
        /* IJsonFormatterResolver[] */
    );
}

JsonFormatterAttribute

JsonFormatterAttribute is lightweight extension point. This is like JSON.NET's JsonConverterAttribute. You can change to use formatter per type and member.

// if serializing, choosed CustomObjectFormatter.
[JsonFormatter(typeof(CustomObjectFormatter))]
public class CustomObject
{
    string internalId;

    public CustomObject()
    {
        this.internalId = Guid.NewGuid().ToString();
    }

    class CustomObjectFormatter : IJsonFormatter<CustomObject>
    {
        public void Serialize(ref JsonWriter writer, CustomObject value, IJsonFormatterResolver formatterResolver)
        {
            formatterResolver.GetFormatterWithVerify<string>().Serialize(ref writer, value.internalId, formatterResolver);
        }

        public CustomObject Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
        {
            var id = formatterResolver.GetFormatterWithVerify<string>().Deserialize(ref reader, formatterResolver);
            return new CustomObject { internalId = id };
        }
    }
}

JsonFormatter can receive parameter and can attach to member. For example, configure DateTime format.

public class Person
{
    public int Age { get; set; }
    public string Name { get; set; }

    [JsonFormatter(typeof(DateTimeFormatter), "yyyy-MM-dd")]
    public DateTime Birth { get; set; }
}

DateTime, DateTimeOffset, TimeSpan is used ISO8601 format in default by ISO8601DateTimeFormatter, ISO8601DateTimeOffsetFormatter, ISO8601TimeSpanFormatter but if you want to configure format, you can use DateTimeFormatter, DateTimeOffsetFormatter, TimeSpanFormatter with format string argument.

Framework Integration

The guide of provide integrate other framework with Utf8Json. For provides customizability of serialization, can be pass the IJsonFormatterResolver by user and does not use CompositeResolver on provided library. For example, AWS Lambda Function's custom serializer.

// with `Amazon.Lambda.Core package`
public class Utf8JsonLambdaSerializer : Amazon.Lambda.Core.ILambdaSerializer
{
    // Note: Default AWS Lambda's JSON.NET Serializer uses special resolver for handle below types.
    // Amazon.S3.Util.S3EventNotification+ResponseElementsEntity
    // Amazon.Lambda.KinesisEvents.KinesisEvent+Record
    // Amazon.DynamoDBv2.Model.StreamRecord
    // Amazon.DynamoDBv2.Model.AttributeValue
    // If you want to serialize these types, create there custom formatter and setup custom resolver.

    readonly IJsonFormatterResolver resolver;

    public Utf8JsonLambdaSerializer()
    {
        // if you want to customize other configuration change your own choose resolver directly
        // (Lambda uses default constructor and does not exists configure chance of DefaultResolver)
        this.resolver = JsonSerializer.DefaultResolver;
    }

    public Utf8JsonLambdaSerializer(IJsonFormatterResolver resolver)
    {
        this.resolver = resolver;
    }

    public void Serialize<T>(T response, Stream responseStream)
    {
        Utf8Json.JsonSerializer.Serialize<T>(responseStream, response, resolver);
    }

    public T Deserialize<T>(Stream requestStream)
    {
        return Utf8Json.JsonSerializer.Deserialize<T>(requestStream, resolver);
    }
}

Utf8Json provides for ASP.NET Core MVC formatter. Utf8Json.AspNetCoreMvcFormatter. This is sample of use it.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().AddMvcOptions(option =>
    {
        option.OutputFormatters.Clear();
        // can pass IJsonFormatterResolver for customize.
        option.OutputFormatters.Add(new JsonOutputFormatter(StandardResolver.Default));
        option.InputFormatters.Clear();
        // if does not pass, library should use JsonSerializer.DefaultResolver.
        option.InputFormatters.Add(new JsonInputFormatter());
    });
}

HTML encodoing only requires UTF8, whatwg/html accepts on 2017-10-06. So don't worry about other encoding:)

Text Protocol Foundation

Utf8Json implements fast itoa/atoi, dtoa/atod. It can be useful for text protocol serialization. For example I'm implementing MySqlSharp that aims fastest MySQL Driver on C#(work in progress yet), MySQL protocol is noramlly text so requires fast parser for text protocol.

Utf8Json.Internal.NumberConverter is Read/Write primitive to bytes. It is public API so you can use if requires itoa/atoi, dtoa/atod algorithm.

byte[] buffer = null; // buffer is automatically ensure.
var offset = 0;
var writeSize = NumberConverter.WriteInt64(ref buffer, offset, 99999);

int readCount;
var value = NumberConverter.ReadInt64(buffer, 0, out readCount);

for Unity

Unity has the JsonUtility. It is well fast but has many limitations, can not serialize/deserialize dictionary or other collections and nullable, can not root array, can not handle null correctly, etc... Utf8Json has no limitation and performance is same or better especialy convert to/from byte[], Utf8Json achieves true no zero-allocation.

In Unity version, added UnityResolver to StandardResolver in default. It enables serialize Vector2, Vector3, Vector4, Quaternion, Color, Bounds, Rect.

.unitypackage is exists in releases page. If you are using IL2CPP environment, requires code generator too, see following section.

Pre Code Generation(Unity/Xamarin Supports)

Utf8Json generates object formatter dynamically by ILGenerator. It is fast and transparently generated at run time. But it does not work on AOT environment(Xamarin, Unity IL2CPP, etc.).

If you want to run on IL2CPP(or other AOT env), you need pre-code generation. Utf8Json.UniversalCodeGenerator.exe is code generator of Utf8Json. It is exists in releases page's Utf8Json.UniversalCodeGenerator.zip that run on win/mac/linux. It is using Roslyn so analyze source code and created by .NET Core for cross platform application.

arguments help:
  -i, --inputFiles=VALUE        [optional]Input path of cs files(',' separated)
  -d, --inputDirs=VALUE         [optional]Input path of dirs(',' separated)
  -o, --output=VALUE            [required]Output file path
  -f, --allowInternal           [optional, default=false]Allow generate internal(friend)
  -c, --conditionalsymbol=VALUE [optional, default=empty]conditional compiler symbol
  -r, --resolvername=VALUE      [optional, default=GeneratedResolver]Set resolver name
  -n, --namespace=VALUE         [optional, default=Utf8Json]Set namespace root name
// Simple usage(directory)
Utf8Json.UniversalCodeGenerator.exe -d "..\src\Shared\Request,..\src\Shared\Response" -o "Utf8JsonGenerated.cs"

If you create DLL by msbuild project, you can use Pre/Post build event or hook your Unity's post/pre process.

<PropertyGroup>
    <PreBuildEvent>
        Utf8Json.UniversalCodeGenerator.exe, here is useful for analyze/generate target is self project.
    </PreBuildEvent>
    <PostBuildEvent>
        Utf8Json.UniversalCodeGenerator.exe, here is useful for analyze target is another project.
    </PostBuildEvent>
</PropertyGroup>

In default, generates resolver to Utf8Json.Resolvers.GeneratedResolver and formatters generates to Utf8Json.Formatters.***. And application launch, you need to set Resolver at first.

// CompositeResolver is singleton helper for use custom resolver.
// Ofcourse you can also make custom resolver.
Utf8Json.Resolvers.CompositeResolver.RegisterAndSetAsDefault(
    // use generated resolver first, and combine many other generated/custom resolvers
    Utf8Json.Resolvers.GeneratedResolver.Instance,

    // set StandardResolver or your use resolver chain
    Utf8Json.Resolvers.StandardResolver.Default,
);

How to Build

Open Utf8Json.sln on Visual Studio 2017(latest) and install .NET Core 2.0 SDK.

Unity Project is using symbolic link. At first, run make_unity_symlink.bat so linked under Unity project. You can open src\Utf8Json.UnityClient on Unity Editor.

Author Info

Yoshifumi Kawai(a.k.a. neuecc) is a software developer in Japan.
He is the Director/CTO at Grani, Inc.
Grani is a mobile game developer company in Japan and well known for using C#.
He is awarding Microsoft MVP for Visual C# since 2011.
He is known as the creator of UniRx(Reactive Extensions for Unity)

Blog: https://medium.com/@neuecc (English)
Blog: http://neue.cc/ (Japanese)
Twitter: https://twitter.com/neuecc (Japanese)

License

This library is under the MIT License.

utf8json's People

Contributors

gitter-badger avatar luochuanyuewu avatar neon-izm avatar neuecc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

utf8json's Issues

Decimal type is being serialized as a string

public class Foo
{
	public decimal Bar { get; set; }
}

JsonSerializer.ToJsonString(new Foo { Bar = 1.23m }); //outputs { "Bar": "1.23" } - should be { "Bar": 1.23 }

JsonSerializer.Deserialize<Foo>("{\"Bar\":1.23}"); //throws JsonParsingException: expected:", actual:. at:8

Can I use DataMemberAttribute when run on IL2CPP?

Hi. Thanks for the very nice lib.
I'd like to use DataMemberAttribute when run on Unity(IL2CPP) but UniversalCodeGenerator is not working for me.

[DataContract]
public class Sample
{
	[DataMember(Name = "sample_id")]
	public string Id;
}

var sample = new Sample(){ Id = "id_001"}

// StandardResolver 
Debug.Log( Utf8Json.JsonSerializer.ToJsonString<Sample>( sample, Utf8Json.Resolvers.StandardResolver.Default ) );
// {"sample_id":"id_001"}

// Use Generated Resolver Test
// Utf8Json.UniversalCodeGenerator.exe -d "..\src\Shared\Request,..\src\Shared\Response" -o "Utf8JsonGenerated.cs"
Debug.Log( Utf8Json.JsonSerializer.ToJsonString<Sample>( sample, Utf8Json.Resolvers.GeneratedResolver.Instance ) );
//{"Id":"id_001"}

Is there any settings I need to change ?

Utf8Json.AspNetCoreMvcFormatter as default serializer sample

To use Utf8Json.AspNetCoreMvcFormatter as the default serializer for all controller actions in a .net standard / core project, is anything required other than installing it? I had to do a lot of things in my .net 4.6 project to have the Utf8Json become the default serializer..

I would sort of expect we need to register / unregister things in the startup code etc..

Nullable DateTime/DateTimeOffset

Hi,
You currently do not provide builtin serializers for nullable Datetime/DateTimeOffset.
I see:

// DateTime can be configure easily so does not register nullable formatter for fallback mechanism.
{typeof(DateTime), ISO8601DateTimeFormatter.Default}, // ISO8601
{typeof(TimeSpan), ISO8601TimeSpanFormatter.Default},
{typeof(DateTimeOffset), ISO8601DateTimeOffsetFormatter.Default},

I know I can add my own IJsonFormatterResolver, but why not provide them out of the box?

DateTime/DateTimeOffset serialization problems

There are currently multiple problems with DT/DTO serialization/deserialization

Description:

Compare ToString(...) functionality of DT/DTO with serialized/deserialized output.
ToString format is {0:yyyy-MM-ddTHH:mm:ss.fffffffK} (https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings)
My current TZ is GMT+2 (Berlin time)

Problems

  1. Loss in precision, any sub-ms precision is lost after the roundtrip, you probably should increase the precision to full precision to allow roundtrips and be compatible
  2. Wrong TZ in DateTime.Now output (not sure where the +1 comes from)

Example output:

DateTime.UtcNow
Reference: 2017-09-27T11:20:15.5785405Z
Output: "2017-09-27T11:20:15.5785405Z" vs "2017-09-27T11:20:15.5780000Z"
DateTimeOffset.UtcNow
Reference: 2017-09-27T11:20:15.6450980+00:00
Output: "2017-09-27T11:20:15.6450980+00:00" vs "2017-09-27T11:20:15.6450000+00:00"
DateTime.Now
Reference: 2017-09-27T13:20:15.6541047+02:00
Output: "2017-09-27T13:20:15.6541047+01:00" vs "2017-09-27T14:20:15.6540000+01:00"
DateTimeOffset.Now
Reference: 2017-09-27T13:20:15.6581082+02:00
Output: "2017-09-27T13:20:15.6581082+02:00" vs "2017-09-27T13:20:15.6580000+02:00"

Repro:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DateTimePrecision
{
    class Program
    {
        static void Main(string[] args)
        {
            TestDTUtcNow();
            TestDTOUtcNow();
            TestDTNow();
            TestDTONow();
        }

        static void TestDTUtcNow()
        {
            Console.WriteLine("DateTime.UtcNow");
            var dto = DateTime.UtcNow;
            var serialized = Utf8Json.JsonSerializer.ToJsonString(dto);
            var deSerialized = Utf8Json.JsonSerializer.Deserialize<DateTime>(serialized);
            var serialized2 = Utf8Json.JsonSerializer.ToJsonString(deSerialized);
            if (serialized2 != serialized)
            {
                Console.WriteLine("Reference: {0:yyyy-MM-ddTHH:mm:ss.fffffffK}", dto);
                Console.WriteLine("Output: {0} vs {1}", serialized, serialized2);
            }
        }


        static void TestDTOUtcNow()
        {
            Console.WriteLine("DateTimeOffset.UtcNow");
            var dto = DateTimeOffset.UtcNow;
            var serialized = Utf8Json.JsonSerializer.ToJsonString(dto);
            var deSerialized = Utf8Json.JsonSerializer.Deserialize<DateTimeOffset>(serialized);
            var serialized2 = Utf8Json.JsonSerializer.ToJsonString(deSerialized);
            if (serialized2 != serialized)
            {
                Console.WriteLine("Reference: {0:yyyy-MM-ddTHH:mm:ss.fffffffK}", dto);
                Console.WriteLine("Output: {0} vs {1}", serialized, serialized2);
            }
        }

        static void TestDTNow()
        {
            Console.WriteLine("DateTime.Now");
            var dto = DateTime.Now;
            var serialized = Utf8Json.JsonSerializer.ToJsonString(dto);
            var deSerialized = Utf8Json.JsonSerializer.Deserialize<DateTime>(serialized);
            var serialized2 = Utf8Json.JsonSerializer.ToJsonString(deSerialized);
            if (serialized2 != serialized)
            {
                Console.WriteLine("Reference: {0:yyyy-MM-ddTHH:mm:ss.fffffffK}", dto);
                Console.WriteLine("Output: {0} vs {1}", serialized, serialized2);
            }
        }


        static void TestDTONow()
        {
            Console.WriteLine("DateTimeOffset.Now");
            var dto = DateTimeOffset.Now;
            var serialized = Utf8Json.JsonSerializer.ToJsonString(dto);
            var deSerialized = Utf8Json.JsonSerializer.Deserialize<DateTimeOffset>(serialized);
            var serialized2 = Utf8Json.JsonSerializer.ToJsonString(deSerialized);
            if (serialized2 != serialized)
            {
                Console.WriteLine("Reference: {0:yyyy-MM-ddTHH:mm:ss.fffffffK}", dto);
                Console.WriteLine("Output: {0} vs {1}", serialized, serialized2);
            }
        }
    }
}

DateTime serialization contains additional '-' character.

It looks like the DateTime serialization to ISO8601 contains an additonal hyphen (-) character.

If I had to guess the formatter is producing a "-5" instead of "5" for the timezone value.

using System;
using Utf8Json;

namespace ConsoleApp6
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var item = new
            {
                Date = DateTime.Now
            };

            var result = JsonSerializer.ToJsonString(item);

            // Produced Output: {"Date":"2017-09-27T14:31:17.1876168-0-5:00"}
            // Correct Output:  {"Date":"2017-09-27T14:31:17.1876168-05:00"}
        }
    }
}

StandardResolver.ExcludeNull not working

Consider this:

using System;
using System.Text;
using Utf8Json;
using Utf8Json.Resolvers;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            char? v1 = null;
            char? v2 = null;

            var bytes = JsonSerializer.Serialize(new { a = v1, b = v2 }, StandardResolver.ExcludeNull);
            var str =  Encoding.UTF8.GetString(bytes);

            Console.WriteLine(str); // outputs {"a":null,"b":null}
            System.Diagnostics.Debugger.Break();
        }
    }
}

System.TypeLoadException thrown when serializing a payload.

I am trying to add the library to Easy.Logger to replace Json.NET for NETSTANDARD2.0 and NET45 (still have to keep Json.NET for NETSTANDARD1.3).

Currently my tests are failing due to:
System.TypeLoadException: 'Type 'Utf8Json.Formatters.Easy_Logger_Extensions_HTTPAppender\+PayloadFormatter1' from assembly 'Utf8Json.Resolvers.DynamicObjectResolverAllowPrivateFalseExcludeNullFalseNameMutateCamelCase, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is attempting to implement an inaccessible interface.'

I am trying to write the JSON directly to a HTTP request stream using:

JsonSerializer.Serialize(stream, payload, StandardResolver.CamelCase);

And here's the object graph:

private sealed class Payload
{
    public int PID { get; set; }
    public string ProcessName { get; set; }
    public string Host { get; set; }
    public string Sender { get; set; }
    public DateTimeOffset TimestampUTC { get; set; }
    public int BatchNo { get; set; }
    public Entry[] Entries { get; set; }
}

private struct Entry
{
    public DateTimeOffset DateTimeOffset { get; set; }
    public string LoggerName { get; set; }
    public string Level { get; set; }		
    public string ThreadID { get; set; }
    public string Message { get; set; }
    public Exception Exception { get; set; }
}

Any idea?
I can push the changes to a temp branch for you to run if you prefer?

Also FYI, the CamelCase mutation of PID should be pid but I am currently getting pID.

Deserialization error when name separator followed by space

Consider this:

using System.Text;
using Utf8Json;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var r1 = JsonSerializer.Deserialize<Dto>(Encoding.UTF8.GetBytes("{\"t\":true}")); // success
            var r2 = JsonSerializer.Deserialize<Dto>(Encoding.UTF8.GetBytes("{\"t\": true}")); // Utf8Json.JsonParsingException: 'expected:true | false, actual:  at:5'
        }
    }

    public class Dto
    {
        public bool t;
    }
}

object[] support

if i serialize object[]{1,2}
and deserialize it back i get doubles instead ints

for example in fastJson it will work because it saves type names in json as well as values

Get-only property will break generated code

public class Hoge {
    public string name;
    public string Name { get { return name; } }
}

will generate like this deserialize code

    public sealed class HogeFormatter : global::Utf8Json.IJsonFormatter<global::Hoge>
    {
        readonly global::Utf8Json.Internal.AutomataDictionary ____keyMapping;
        readonly byte[][] ____stringByteKeys;

        public HogeFormatter()
        {
            this.____keyMapping = new global::Utf8Json.Internal.AutomataDictionary()
            {
                { JsonWriter.GetEncodedPropertyNameWithoutQuotation("Name"), 0},
                { JsonWriter.GetEncodedPropertyNameWithoutQuotation("name"), 1},
            };

            this.____stringByteKeys = new byte[][]
            {
                JsonWriter.GetEncodedPropertyNameWithBeginObject("Name"),
                JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator("name"),

            };
        }

        public global::Hoge Deserialize(ref JsonReader reader, global::Utf8Json.IJsonFormatterResolver formatterResolver)
        {
            if (reader.ReadIsNull())
            {
                return null;
            }


            var __Name__ = default(string);
            var __Name__b__ = false;
            var __name__ = default(string);
            var __name__b__ = false;

            var ____count = 0;
            reader.ReadIsBeginObjectWithVerify();
            while (!reader.ReadIsEndObjectWithSkipValueSeparator(ref ____count))
            {
                var stringKey = reader.ReadPropertyNameSegmentRaw();
                int key;
                if (!____keyMapping.TryGetValueSafe(stringKey, out key))
                {
                    reader.ReadNextBlock();
                    goto NEXT_LOOP;
                }

                switch (key)
                {
                    case 0:
                        __name__ = reader.ReadString();
                        __name__b__ = true;
                        break;
                    default:
                        reader.ReadNextBlock();
                        break;
                }

                NEXT_LOOP:
                continue;
            }

            var ____result = new global::Hoge();
            if(__name__b__) ____result.name = __name__;

            return ____result;
        }
     }

I think it should be case 1, not case 0 since 0 is for Name property.

Issue with custom serialization for dictionary key

I have some class T which have attribute [JsonFormatter(typeof(F))].

Everything works perfect with serialization/deserialization for Dictionary<T, T1>.

   var dict = new Dictionary<T, T1>();
   // skip init
   var serializedString = Utf8Json.JsonSerializer.ToJsonString(dict);
   var deserializedObject = Utf8Json.JsonSerializer.Deserialize<Dictionary<Key, string>>(serializedString);

But when I'm trying to use class T2 something goes wrong and serializer don't apply custom serialization for type T.

public class T2 : Dictionary<T, T1> 
{ 
}

   var dict = new T2();
   // skip init
   var serializedString = Utf8Json.JsonSerializer.ToJsonString(dict);
   // code failed here
   var deserializedObject = Utf8Json.JsonSerializer.Deserialize<T2>(serializedString);

Error:

---
System.ArgumentException : not of type: Tests.Utils.JsonTests+Key
Parameter name: key
---
at System.Collections.Generic.Dictionary`2[TKey,TValue].ToTKey (System.Object key) [0x0001c] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:647 
  at System.Collections.Generic.Dictionary`2[TKey,TValue].System.Collections.IDictionary.Add (System.Object key, System.Object value) [0x00000] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:671 
  at Utf8Json.Formatters.NonGenericDictionaryFormatter`1[Tests.Utils.JsonTests+TestKey].Deserialize (Utf8Json.JsonReader& reader, IJsonFormatterResolver formatterResolver) [0x00053] in C:\Data\UnityProjects\QPlatform\Assets\TGClient\Core\Utils\Json\Utf8Json\LibraryCode\Formatters\DictionaryFormatter.cs:363 
  at Utf8Json.JsonSerializer.Deserialize[TestKey] (System.Byte[] bytes, Int32 offset, IJsonFormatterResolver resolver) [0x00022] in C:\Data\UnityProjects\QPlatform\Assets\TGClient\Core\Utils\Json\Utf8Json\LibraryCode\JsonSerializer.cs:213 
  at Utf8Json.JsonSerializer.Deserialize[TestKey] (System.Byte[] bytes, IJsonFormatterResolver resolver) [0x00004] in C:\Data\UnityProjects\QPlatform\Assets\TGClient\Core\Utils\Json\Utf8Json\LibraryCode\JsonSerializer.cs:199 
  at Utf8Json.JsonSerializer.Deserialize[TestKey] (System.String json, IJsonFormatterResolver resolver) [0x0000d] in C:\Data\UnityProjects\QPlatform\Assets\TGClient\Core\Utils\Json\Utf8Json\LibraryCode\JsonSerializer.cs:189 
  at Utf8Json.JsonSerializer.Deserialize[TestKey] (System.String json) [0x00007] in C:\Data\UnityProjects\QPlatform\Assets\TGClient\Core\Utils\Json\Utf8Json\LibraryCode\JsonSerializer.cs:184 
  at Tests.Utils.JsonTests.Utf8JsonCustomNestedDictSerialize () [0x00035] in C:\Data\UnityProjects\QPlatform\Assets\Tests\UtilsTests\JsonTest.cs:594 
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d0] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222

Code example is here

DateTimeOffset parsing problems in complex objects

Hi, there are still some problems with DTO parsing in complex object. I think this only happens if no sub-second precision values are available. Exception is:
---- System.InvalidOperationException : invalid datetime format. value:1970-01-01T09:15:10+00:00

using System;
using Utf8Json.Resolvers;

namespace ConsoleApp9
{
    class Program
    {
        static void Main(string[] args)
        {
            var input = "{\"Flag\":false,\"StatusDate\":\"1970-01-01T09:15:10+00:00\"}";
            var deserialized = Utf8Json.JsonSerializer.NonGeneric.Deserialize(typeof(Test), input, StandardResolver.ExcludeNull);
        }
    }

    public class Test
    {
        public bool Flag { get; set; }
        public DateTimeOffset? StatusDate { get; set; }
    }
}

Add DeserializeDynamic

current Deserialize<dynamic> deserialize json map to IDictionary<string, object>.
But it does not allow dot syntax(foo["bar"]["baz"])
new API DeserializeDynamic that deserialize json map to ExpandoObject.

Question: SnakeCase, Dictionary Keys, Custom Resolver.

We are looking into replacing the default serializer of Foundatio (https://github.com/FoundatioFx/Foundatio) as well as Exceptionless (https://github.com/exceptionless/Exceptionless). For Foundatio it's pretty easy to do, but for Exceptionless there are a few questions that need to be resolved and one of them is snake casing. I see that there is the ability to do snake casing and creating your own snake case resolver. However, here are the questions that I had:

SnakeCase

  1. We have a client that sends us metric ... ton of events and it's currently using a custom version of snake case (created before json.net had it). We can't change this out and we have to support it.
    a. How much overhead do you typically see when using a naming convention vs the standard naming? I didn't really see any benchmarks.
    b. How close is the snake case implementation to the json.net snake case strategy? I could see where you might have different systems and you want them to work together.
    c. I didn't see anything in the readme or the code that I looked, but is it possible to exclude naming conventions on dictionary keys? JSON.NET has this built in so you don't morph dictionary keys.

Custom Converters.

  1. We have a simple event model and we through all our known models into a dictionary with a known key (e.g., @error, @request). Currently in JSON.NET we can specify the type to deserialize a model to based on the key with a custom converter. Is anything like this possible? I didn't see anything like this when I was looking around.

Unknown Data Types

  1. What happens when an unknown data type can't be converted? Does it stay in some JSON type model like a JToken?

Dynamic Contract Resolver

  1. We have a dynamic contract resolver (https://github.com/exceptionless/Exceptionless/blob/master/src/Exceptionless.Core/Serialization/ElasticDynamicTypeContractResolver.cs) where we basically inspect the type being deserialized and if it's not from our library we use the default serializer behavior instead of our naming conventions etc. I need to see if this is possible (at least short term until we get on NEST 6.0 (Elasticsearch) which will be using your library as well ;)).

Failed to deserialize collection that contains null value

Deserialize object failed, when following condition met

  1. Collection contains null value
  2. Don't specify exact type to Deserialize method, and fallback resolver for object is used.

Following code reproduce problems. (Confirmed by v1.0.3.1)

var item = new string[] { "aaa", null, "bbb" };

//Serialize to JSON string
//  -> ["aaa",null,"bbb"]
var json = JsonSerializer.ToJsonString(item);

//Pattern1: OK
//  When specify explicit type
var result1 = JsonSerializer.Deserialize<string[]>(json);

//Pattern2: NG:
//  When specify dynamic type (or specify object[])
var result2 = JsonSerializer.Deserialize<dynamic>(json);

StackTrace

Unhandled Exception: Utf8Json.JsonParsingException: expected:,, actual:n at:11
  at Utf8Json.JsonReader.ReadIsValueSeparatorWithVerify()
  at Utf8Json.JsonReader.ReadIsEndObjectWithSkipValueSeparator(Int32 & count)
  at Utf8Json.Formatters.PrimitiveObjectFormatter.Deserialize(JsonReader & reader, IJsonFormatterResolver formatterResolver)
  at Utf8Json.Formatters.DynamicObjectTypeFallbackFormatter.Deserialize(JsonReader & reader, IJsonFormatterResolver formatterResolver)

Use `JsonUtf8Encoding : Encoding`

Escaping string character is hurt of performance of JSON serialization.
It is possible to reduce escape cost by creating custom UTF8 Encoding that includes JSON encoding/decoding.
for invoke internal FastAllocateString, it is necessary to inherit Encoding.

public class JsonUtf8Encoding : Encoding
{
    #region decode(for reader)

    // (Encoding.GetString) -> GetCharCount -> (FastAllocateString) -> GetChars

    public override int GetCharCount(byte[] bytes, int index, int count)
    {
        // return CharCount is \" (.+) \", (.+) group unescaped.
        if (bytes[index] != '\"') throw new InvalidOperationException();

        throw new NotImplementedException();
    }

    public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
    {
        throw new NotImplementedException();
    }

    #endregion

    #region encode(for writer)

    // should use GetByteCount? too large?

    public override int GetMaxByteCount(int charCount)
    {
        return Encoding.UTF8.GetMaxByteCount(charCount) * 2; // worst case, escaped.
    }

    public override unsafe int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex)
    {
        int byteCount = bytes.Length - byteIndex;

        fixed (char* pChars = s)
        fixed (byte* pBytes = bytes)
        {
            return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount);
        }
    }

    public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
    {
        throw new NotImplementedException();
    }

    #endregion

    public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
    {
        throw new NotSupportedException();
    }

    public override int GetByteCount(char[] chars, int index, int count)
    {
        throw new NotSupportedException();
    }

    public override int GetMaxCharCount(int byteCount)
    {
        throw new NotSupportedException();
    }
}

Also, it is necessary to implement efficient UTF 8 encoding/decoding.
I found this article.
http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
If there are any other good examples, please let me know.

Support for ShouldSerialize pattern

Most C# serializers support the ShouldSerializeXXX pattern to programmatically decide if a value should be serialized.
Example from JSON.NET (https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm):

public class Employee
{
    public string Name { get; set; }
    public Employee Manager { get; set; }

    public bool ShouldSerializeManager()
    {
        // don't serialize the Manager property if an employee is their own manager
        return (Manager != this);
    }
}

It is for example used quite often to not serialize empty collections, e.g.

using System.Collections.Generic;

namespace ConsoleApp9
{
    class Program
    {
        static void Main(string[] args)
        {
            var test = new Test();
            var text = Utf8Json.JsonSerializer.ToJsonString(test);
        }
    }

    public class Test
    {
        public List<string> Data { get; set; } = new List<string>();

        public bool ShouldSerializeData()
        {
            return Data?.Count > 0;
        }
    }
}

It would be great if support for this could be added.

Error handle

hi, I like this lib very much, real fast indeed, and prepare replace json.net for my new project.
I wonder if Uft8Json can let me know which property or field failed when deserialize.
for example.
a class contains Id property, int type, user provides wrong value "abc", must throw exception with additional fail reason (like ErrorEventArgs in json.net, through errorArg.ErrorContext.Path)

Functionality of StandardResolver.AllowPrivate

Hi,
AllowPrivate currently solves two problems which might conflict:

  1. You can serialize private/protected members
  2. You can deserialize private/protected members, including private/protected setters

The example at the bottom serializes the empty Dictionary

"{\"Id\":\"b63cff84-e203-4b97-8c71-bf95f099b26d\",\"Dictionary\":{}}"

and fails during deserialization because GUID is not supported as Key. Which is perfectly fine.
The original intention was that not Dictionary is serialized, but the protected setter of Id can be used, But since AllowPrivate serializes the protected field Dictionary too, I need to annotate it with [IgnoreDataMember] so it won't get serialized.

It might be more useful to split the StandardResolver configuration in multiple parts:

  • IncludeProperties (default, can be skipped, but should be an independent configuration anyway)
  • IncludeFields
  • AllowPrivate

What I'm suggesting is, that you need an extra level of configuration called AllowFields which serializes fields.

This results in the following basic combinations:

  1. StandardResolver.IncludeProperties: Default, serializes public properties
  2. StandardResolver.IncludePropertiesAllowPrivate: Serializes Properties, including private/protected properties including private/protected setters/getters
  3. StandardResolver.IncludeFields: Only serializes public fields
  4. StandardResolver.IncludeFieldsAllowPrivate: Only serializes fields, including private protected
  5. StandardResolver.IncludePropertiesIncludeFieldsAllowPrivate: Serializes private/protected/public fields and properties

Obviously this gets quite a bit longer with ExcludeNull, NameMutate functionality etc.

So it might be useful to put all those options in an enum annotated with [Field] and add a factory method creating the proper StandardResolver from this enum (e.g. CreateJsonFormatterResolver(enum)) and cache it in a static field somewhere. Then you can simply build your own formatter bases on the enum, e.g. IncludeProperties | IncludeFields | ExcludeNull | AllowPrivate | CamelCase (which would then serialize all private/protected/public fields/properties which are not null into camel case.

I understand this configuration might be a breaking change to the current AllowPrivate behaviour.

    class Program
    {
        static void Main(string[] args)
        {
            MyTest t = new MyTest();
            t.SetId(Guid.NewGuid());

            var output = JsonSerializer.ToJsonString(t, StandardResolver.AllowPrivate);
            var input = JsonSerializer.Deserialize<MyTest>(output, StandardResolver.AllowPrivateExcludeNull);
            Debug.Assert(t.Id == input.Id);
        }
    }

    public abstract class TestBase
    {
        protected Dictionary<Guid, String> Dictionary = new Dictionary<Guid, string>();
        public Guid Id { get; protected set; }
    }

    public class MyTest : TestBase
    {
        public void SetId(Guid id)
        {
            Id = id;
        }
    }

Serializing a type with a fixed field causes an InvalidProgramException

Consider this structure:

        public struct CharArray16
        {
            public fixed byte Values[16];
        }

When I try to serialize an instance of this type, for example:

CharArray16 ca16 = new CharArray16();
JsonSerializer.Serialize(ca16);

I get this exception:

System.InvalidProgramException: 'Common Language Runtime detected an invalid program.'

This is on .NET Core 2.0.

AllowPrivate gets ignored in Unity?

I finally migrated everything to Utf8Json. So far it went smooth, but there seems to be an issue with the AllowPrivate Deserialisation in Unity. (using 2017.3.0f3).

JsonSerializer.SetDefaultResolver(Utf8Json.Resolvers.StandardResolver.AllowPrivate);
   TextAsset database = assetBundleMap.LoadAsset<TextAsset>("zones");
   jsonDB.Zones = JsonSerializer.Deserialize<JsonDB>(database.text).Zones; 

This is the complete same code which I am using server side via a .net Standard 2.0 dll of your Utf8Json.

But in Unity (using no AOT - I am building for Mac Universal for now) the private fields are all ignored - its the same classes I also use on the server - sharing same gamedata dll.

I tried to check if public fields work (to be sure I am not doing anything wrong) and as soon as I Deserialise public fields its working fine - but the AllowPrivate is completely ignored.

Any idea - because right now I am stuck and can't build the client again - since it doesnt get any data from my json files :). Must be some kind of issues with reflection in unity.

Thanks a lot,
Oliver

Serialization with type name handling.

How can i get the same behavior, like in Json.Net with following code

new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All
};

It so so helpful in situations like this:

public class A
{
    public B Base;
}

public class B 
{
}

public class C
{
}
...
var a = new A();
a.B = new C();
var str = Serialize(a);
a = Deserialize(str);

// expected typeof(C) but was typeofB()
Assert.AreEquals(a.B.GetType(), typeof(C))

Any suggestions?

Deserialize into existing object

is it possible to fill existing object instead creating new one? something like
void JsonSerializer.Deserialize(string json,T output)

IL2CPP call not supported

Using the latest release of Utf8Json, in our Unity Game on iOS we get the following error :

NotSupportedException: /Users/builduser/buildslave/unity/build/External/il2cpp/il2cpp/libil2cpp/icalls/mscorlib/System.Reflection.Emit/AssemblyBuilder.cpp(20) : Unsupported internal call for IL2CPP:AssemblyBuilder::basic_init - System.Reflection.Emit is n

Utf8Json.Resolvers.DynamicObjectResolver..cctor ()
Rethrow as TypeInitializationException: The type initializer for 'Utf8Json.Resolvers.Internal.DynamicObjectResolverAllowPrivateFalseExcludeNullFalseNameMutateOriginal' threw an exception.
Utf8Json.Resolvers.DynamicObjectResolver..cctor ()
Rethrow as TypeInitializationException: The type initializer for 'Utf8Json.Resolvers.DynamicObjectResolver' threw an exception.
Rethrow as TypeInitializationException: The type initializer for 'Utf8Json.Resolvers.Internal.DefaultStandardResolver.InnerResolver' threw an exception.
Rethrow as TypeInitializationException: The type initializer for 'Utf8Json.Resolvers.Internal.DefaultStandardResolver' threw an exception.
Rethrow as TypeInitializationException: The type initializer for 'Utf8Json.Resolvers.StandardResolver' threw an exception.

This happens when calling Utf8Json.JsonSerializer.Serialize with a file stream and a data structure containing various element including a structured List.

Class inheritance not supported in deserialization?

Hey there :)

I was trying to switch over from Json.NET to your utf8Json.

But I found out that it does not support inheritance of classes?

Example:

public abstract class JsonDBItem
    {
        public int ID { get { return _id; } }
        private int _id = -1;
    }

   public class Card: JsonDBItem
    {
        public string Name { get { return name; } }
        private string name = 0;
    }

Json:
"_id": 1,
"name": "DummyName"

I have AllowPrivate set to true - so the Json file will deserialize the name, but not the properties from the inherited class JsonDBItem -> ID is still empty - in Json.NET this worked. Any chance to make this possible in utf8json?

Thanks alot,
Oliver

Deserializing type object with generated serializer

First of all, thank you for this fantastic library. It has shown huge improvements for me over other libraries I've tried.

I am in the process of converting a Unity project that currently uses LitJson to use Utf8Json, due to big garbage creation and memory savings my testing has shown Utf8Json provides. One of the requirements of my project is that it must be compatible with existing JSON files as input, which means I can't use a solution that requires I re-serialize or modify the input data structure in any way. Because my Unity build uses IL2CPP, I am using the Universal Code Generator utility, but I have a couple of questions.

  1. I'm unclear how I would, or if its even possible to, deserialize to a generic object. Right now, when I run the RegisterAndSetAsDefault function to register my Generated Resolver, I use the following line:

Utf8Json.Resolvers.CompositeResolver.RegisterAndSetAsDefault (Utf8Json.Resolvers.GeneratedResolver.Instance, Utf8Json.Resolvers.BuiltinResolver.Instance);

The second parameter is what I found I needed to add to deserialize primitive objects. However, if I add the Standard Resolver as a parameter, as is shown in the example usage of the Universal Code Generator utility, I receive the same error at runtime on an iOS device as if I don't pre generate code, which is the NotSupportedException regarding System.Reflection.Emit.

Is there a different parameter I have to add in order to support the object type, or do I have to modify my generated resolver class to accommodate it? If so, how so?

  1. Some of the objects that are currently in the JSON files I am attempting to deserialize use a custom deserializer to deserialize different objects, in the same array, based on the first value that was stored in a "type" field. For example, an example JSON may look like this:
[
   {
      "___TYPE" : "DisplayAction",
      "image" : "testimage.png",
      "name" : "CatPic",
      "enabled" : false
   },
   {
      "___TYPE" : "PlaySoundAction",
      "sound" : "meow.wav",
      "name" : "meow",
      "duration" : 2.2,
      "delay" : 1.0,
      "enabled" : false,
      "sequence" : 
      [
         {
            "___TYPE" : "SoundSequence",
            "onStart" : null
            "onFinish" : null
            "times" : 2
         }
      ]
   }
]

The custom deserializer deserializes each of these objects into their respective types; in this case, it would be a DisplayAction object and a PlaySoundAction object. Is there a way to deserialize something like this?

Within the Unity Editor (I can't deserialize to Object yet on iOS because of my previous question), if I simply deserialize this into a List of type Object, the result that I have seen is of type Dictionary<string, object>. Is there a way, after deserialization, to then convert this to the final object type, such as (from the example above) DisplayAction or SoundAction?

Thank you for your help.

bson support

would be good replacement for protobuf if add bson support

Enhancing the Camel Case mutation

Given the following:

public sealed class Entry
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int ThreadID { get; set; }
}

var json1 = Utf8Json.JsonSerializer.ToJsonString(payload, StandardResolver.AllowPrivateCamelCase);
var json2 = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
    ContractResolver = new CamelCasePropertyNamesContractResolver()
});

    Utf8Json.JsonSerializer.PrettyPrint(json1).Dump("UTF8Json");
    Utf8Json.JsonSerializer.PrettyPrint(json2).Dump("Json.NET");

Returns the following:

UTF8Json
{
  "iD": 1,
  "name": "foo",
  "threadID": 2
} 

Json.NET
{
  "id": 1,
  "name": "foo",
  "threadID": 2
}

ProxyObjectResolver

This is the concept of easy to define external type's custom formatter.

public class ProxyObjectResolver : IJsonFormatterResolver
{
    public static readonly ProxyObjectResolver Instance = new ProxyObjectResolver();

    ProxyObjectResolver()
    {
    }

    public IJsonFormatter<T> GetFormatter<T>()
    {
        return FormatterCache<T>.formatter;
    }

    public static void RegisterProxy<TRegister, TProxy>()
    {
        // TODO:map type and generate dynamic formatter.
    }

    static class FormatterCache<T>
    {
        public static IJsonFormatter<T> formatter = null;
    }
}

Error serializing linq IEnumerable

Hi,
the following snippet throws with a Typeload Exception:

System.TypeLoadException occurred
  HResult=0x80131522
  Message=Type "Utf8Json.Formatters.System_Linq_Enumerable\+WhereSelectListIterator`2\[\[WhereIteratorRepro_Test\, WhereIteratorRepro\]\,\[System_Guid\, mscorlib\]\]Formatter1" in der Assembly "Utf8Json.Resolvers.DynamicObjectResolverAllowPrivateFalseExcludeNullFalseNameMutateOriginal, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" tries, eine Schnittstelle zu implementieren, auf die nicht zugegriffen werden kann.
  Source=Utf8Json
  StackTrace:
   at Utf8Json.JsonFormatterResolverExtensions.GetFormatterWithVerify[T](IJsonFormatterResolver resolver)
   at Utf8Json.JsonSerializer.Serialize[T](T value, IJsonFormatterResolver resolver)
   at Utf8Json.JsonSerializer.NonGeneric.Serialize(Type type, Object value, IJsonFormatterResolver resolver)
   at Utf8Json.JsonSerializer.NonGeneric.Serialize(Object value)

Sorry about the german exception but the error is basically typeloadexception for an inaccessible interface.

using System;
using System.Collections.Generic;
using System.Linq;

namespace WhereIteratorRepro
{
    class Program
    {
        static void Main(string[] args)
        {

            var list = new List<Test>
            {
                new Test {Value = "Hello World", Id = Guid.NewGuid()},
                new Test {Value = "Hello Universe", Id = Guid.NewGuid()}
            };

            var select = list.Select(a => a.Id);
            var serialized = Utf8Json.JsonSerializer.NonGeneric.Serialize(select); // throws
            
        }

    }

    public class Test
    {
        public Guid Id { get; set; }
        public string Value { get; set; }
    }
}

Resolvers

StandardResolver
StandardResolverWithOmitNullProperty
StandardResolverWithConvertSnakeCase
StandardResolverWithOmitNullPropertyAndConvertSnakeCase

Doesn't work with UWP C# projects.

I have used this to great effect in my WPF C# project but the nuget package doesn't seem to support C# UWP projects.

Is there any chance that this could be fixed?

I can't use this sadly if it only works on some of my projects.

Also MessagePack doesn't work either...

OnDeserialized Attribute?

Is this attribute already supported. If not, would it be difficult to implement it? So you can call a function after deserialisation. I used this in Json.net for setting some object references afterwards.

Thanks,
Oliver

Support for "$type"

Is there a way to support something like "$type"?
This would allow the following scenario:


    public class A { }

    public class B :A{ }

    public class C : A { }

public void Method()
{
         var list = new List<A>
            {
                new B(),
                new C()
            };
            var bytes = Utf8Json.JsonSerializer.Serialize(list);
            var dList = Utf8Json.JsonSerializer.Deserialize<List<A>>(bytes);

}

Replace tech empower

https://github.com/TechEmpower/FrameworkBenchmarks/blob/master/frameworks/CSharp/aspnetcore/Benchmarks/Middleware/JsonMiddleware.cs

namespace Benchmarks.Middleware
{
    public struct JsonMessage
    {
        public string message;
    }

    public class JsonMiddleware
    {
        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.Json));
        private const int _bufferSize = 27;

        private readonly RequestDelegate _next;

        public JsonMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public Task Invoke(HttpContext httpContext)
        {
            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
            {
                httpContext.Response.StatusCode = 200;
                httpContext.Response.ContentType = "application/json";
                httpContext.Response.ContentLength = _bufferSize;

                var msg = new JsonMessage { message = "Hello, World!" };
                Utf8Json.JsonSerializer.Serialize(httpContext.Response.Body, ref msg);

                return Task.CompletedTask;
            }

            return _next(httpContext);
        }
    }

    public static class JsonMiddlewareExtensions
    {
        public static IApplicationBuilder UseJson(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<JsonMiddleware>();
        }
    }
}

AllowPrivateWithExcludeNull not deserializing private/protected member

Hi,
StandardResolver.AllowPrivateExcludeNull does not properly deserialize private/protected members, StandardResolver.AllowPrivate works.

In the repro below input.Id is the default value

Repro:

namespace AllowPrivate
{
    class Program
    {
        static void Main(string[] args)
        {
            MyTest t = new MyTest();
            t.SetId(Guid.NewGuid());

            var output = JsonSerializer.ToJsonString(t, StandardResolver.AllowPrivateExcludeNull);
            var input = JsonSerializer.Deserialize<MyTest>(output, StandardResolver.AllowPrivateExcludeNull);
            Debug.Assert(t.Id == input.Id);
        }
    }

    public abstract class TestBase
    {
        public Guid Id { get; protected set; }
    }

    public class MyTest : TestBase
    {
        public void SetId(Guid id)
        {
            Id = id;
        }
    }
}

Can not run UniversalCodeGenerator for Mac

Hi,

Thanks for very nice library.
I'd like to use this with Unity, and I'm working on Mac (Sierra) but UniversalCodeGenerator is not working for me.
Here is the error message.

% ./Utf8Json.UniversalCodeGenerator

Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'System.Runtime.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

Can you help me?

Missing hyphen '-' character in deserialized string

I used JsonSerializer.Deserialize method to deserialize an object. One field containing the value

"\u0428\u0440\u0438-\u041b\u0430\u043d\u043a\u0430"

was deserialized to "ШриЛанка", i.e. hyphen was skipped.

Deserialization json string return a null list instead of an empty list

public class SomeClass
{
    public SomeClass()
    {
        AnotherClass = new List<AnotherClass>();
    }

    public List<AnotherClass> AnotherClass { get; set; }
}
public class AnotherClass
{
    public int Foo { get; set;}
}
string jsonString = "[{}]"

List<SomeClass> _list = new List<SomeClass>();

_list = JsonSerializer.Deserialize<List<SomeClass>>(jsonString);

The list of another class inside _list object equal to null, not an empty list.

JSON Comments support

JSON Comments is invalid JSON Format but used widely(for example, VSCode - settings.json) and also supports JSON.NET. Utf8Json suports both single-line comment and multi-line comment.

{
    // allow single line comment
    "foo": true, // trailing
    /*
      allow
      multi
      line
      comment
    */
    "bar": 999 /* trailing */
}

commit:
5e42348

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.