rpgmaker / NetJSON

Faster than Any Binary? Benchmark: http://theburningmonk.com/2014/08/json-serializers-benchmarks-updated-2/
MIT License
231 stars 29 forks source link

Serialize overload #153

Closed LorenzoRuggeri closed 7 years ago

LorenzoRuggeri commented 7 years ago

You're my hero. 🥇

First, the grats! Then the issue.

Latest benchmark:

---[ Best performers for Elapsed Time ]--- ---[ Mercury.Cache.UnitTest.Data.ComplexObject ]--- Formatter: WireFormatter ObjectGraph: Mercury.Cache.UnitTest.Data.ComplexObject Elapsed time: 0,1786 ms Size: 33114

Formatter: CompactFormatter ObjectGraph: Mercury.Cache.UnitTest.Data.ComplexObject Elapsed time: 0,3868 ms Size: 34482

Formatter: BinaryFormatter ObjectGraph: Mercury.Cache.UnitTest.Data.ComplexObject Elapsed time: 0,4016 ms Size: 34480

Formatter: NetJsonFormatter ObjectGraph: Mercury.Cache.UnitTest.Data.ComplexObject Elapsed time: 1,5467 ms Size: 43295

---[ Mercury.Cache.UnitTest.Data.ComplexType ]--- Formatter: NetJsonFormatter ObjectGraph: Mercury.Cache.UnitTest.Data.ComplexType Elapsed time: 0,0018 ms Size: 36

Formatter: WireFormatter ObjectGraph: Mercury.Cache.UnitTest.Data.ComplexType Elapsed time: 0,0037 ms Size: 140

Formatter: BinaryFormatter ObjectGraph: Mercury.Cache.UnitTest.Data.ComplexType Elapsed time: 0,0294 ms Size: 225

Formatter: CompactFormatter ObjectGraph: Mercury.Cache.UnitTest.Data.ComplexType Elapsed time: 0,0299 ms Size: 227

---[ System.Collections.Generic.Dictionary2[System.String,Mercury.Cache.UnitTest.Data.ComplexType] ]--- Formatter: NetJsonFormatter ObjectGraph: System.Collections.Generic.Dictionary2[System.String,Mercury.Cache.UnitTest.Data.ComplexType] Elapsed time: 0,0035 ms Size: 45

Formatter: WireFormatter ObjectGraph: System.Collections.Generic.Dictionary`2[System.String,Mercury.Cache.UnitTest.Data.ComplexType] Elapsed time: 0,0158 ms Size: 484

Formatter: CompactFormatter ObjectGraph: System.Collections.Generic.Dictionary`2[System.String,Mercury.Cache.UnitTest.Data.ComplexType] Elapsed time: 0,1136 ms Size: 1732

Formatter: BinaryFormatter ObjectGraph: System.Collections.Generic.Dictionary`2[System.String,Mercury.Cache.UnitTest.Data.ComplexType] Elapsed time: 0,1421 ms Size: 1730

---[ System.Byte[] ]--- Formatter: WireFormatter ObjectGraph: System.Byte[] Elapsed time: 0,0012 ms Size: 69

Formatter: NetJsonFormatter ObjectGraph: System.Byte[] Elapsed time: 0,0029 ms Size: 90

Formatter: BinaryFormatter ObjectGraph: System.Byte[] Elapsed time: 0,0093 ms Size: 92

Formatter: CompactFormatter ObjectGraph: System.Byte[] Elapsed time: 0,0125 ms Size: 94

Encoding and decoding a byte array result in a slower performance but... hey, it's JSON! So, it's ended up for every DTO which don't use byte or char yours is... "faster than any binary serializer!". I don't know if what tear down the benchmark is the static class inside ComplexObject or what else. For sure I'll try to mix-up some test classes to see what I get.

A side note... changing the implementation for

    public byte[] Serialize<T>(T value)
    {
        return Encoding.UTF8.GetBytes(NetJSON.NetJSON.Serialize(value.GetType(), value));
    }

    public T Deserialize<T>(byte[] bytes)
    {
        return (T)NetJSON.NetJSON.Deserialize(typeof(T), Encoding.UTF8.GetString(bytes));
    }

in

    public byte[] Serialize<T>(T value)
    {
        return Encoding.UTF8.GetBytes(NetJSON.NetJSON.Serialize<T>(value, new NetJSON.NetJSONSettings() { UseEnumString = true, IncludeTypeInformation = true }));
    }

    public T Deserialize<T>(byte[] bytes)
    {
        return NetJSON.NetJSON.Deserialize<T>(Encoding.UTF8.GetString(bytes), new NetJSON.NetJSONSettings() { UseEnumString = true, IncludeTypeInformation = true });
    }

as you suggested, results in a byte array of zero length, in serialization.

rpgmaker commented 7 years ago

You are to use the includetypeinformation as static and not a property of setting yet. It is not ported over to setting class at this time.

Thanks,

rpgmaker commented 7 years ago

Did you test it yet using the static property just for the IncludeTypeInformation.

So you will end up with a setting class but use IncludeTypeInformation separately as

NetJSON.IncludeTypeInformation = true

Thanks,

LorenzoRuggeri commented 7 years ago

Excuse me, I did the testing using those two classes.

The first one works.

[System.Diagnostics.DebuggerStepThrough]
class NETJsonFormatter : IBinarySerializer
{
    public NETJsonFormatter() { }

    static bool Initialize()
    {
        NetJSON.NetJSON.IncludeFields = true;
        NetJSON.NetJSON.IncludeTypeInformation = true;
        NetJSON.NetJSON.UseEnumString = true;
        return true;
    }

    static bool Initialized = Initialize();

    public byte[] Serialize<T>(T value)
    {
        return Encoding.UTF8.GetBytes(NetJSON.NetJSON.Serialize(value.GetType(), value));
    }

    public T Deserialize<T>(byte[] bytes)
    {
        return (T)NetJSON.NetJSON.Deserialize(typeof(T), Encoding.UTF8.GetString(bytes));
    }

    public object Deserialize(Stream serializationStream)
    {
        return NetJSON.NetJSON.Deserialize(typeof(object), Encoding.UTF8.GetString(serializationStream.ToByteArray()));
    }

    public void Serialize(Stream serializationStream, object graph)
    {
        byte[] serialized = Encoding.UTF8.GetBytes(NetJSON.NetJSON.Serialize(graph.GetType(), graph));
        serializationStream.Write(serialized, 0, serialized.Length);
    }
}

The next one is not working:

class NETJsonFormatter : IBinarySerializer
{
    public NETJsonFormatter() { }

    static bool Initialize()
    {
        NetJSON.NetJSON.IncludeTypeInformation = true;
        return true;
    }

    static bool Initialized = Initialize();

    public byte[] Serialize<T>(T value)
    {
        return Encoding.UTF8.GetBytes(NetJSON.NetJSON.Serialize<T>(value, new NetJSON.NetJSONSettings() { UseEnumString = true }));
    }

    public T Deserialize<T>(byte[] bytes)
    {
        return NetJSON.NetJSON.Deserialize<T>(Encoding.UTF8.GetString(bytes), new NetJSON.NetJSONSettings() { UseEnumString = true });
    }

    public object Deserialize(Stream serializationStream)
    {
        return NetJSON.NetJSON.Deserialize(typeof(object), Encoding.UTF8.GetString(serializationStream.ToByteArray()));
    }

    public void Serialize(Stream serializationStream, object graph)
    {
        byte[] serialized = Encoding.UTF8.GetBytes(NetJSON.NetJSON.Serialize(graph.GetType(), graph));
        serializationStream.Write(serialized, 0, serialized.Length);
    }
}

Please note I can't find IncludeFields in the settings class.

The error I'm getting is:

NetJSON.NetJSONInvalidJSONException: Input is not a valid JSON. in NetJSON.Internals.SerializerUtilities.ThrowIfInvalidJSON(String json, Char chr) in ComplexObjectClass.ReadComplexObject(String , NetJSONSettings ) in Mercury.Cache.Serialization.Engine.Deserialize[T](Byte[] value, Formatter formatter)

Thanks!

rpgmaker commented 7 years ago

You are right. I don't have it, I must have forgot. You can use includeFields as static I guess. Can you print the JSON, is it empty? You should not be getting invalid JSON.

Thanks,

LorenzoRuggeri commented 7 years ago

The string returned is empty.

I corrected the implementation as follows:

class NETJsonFormatter : IBinarySerializer
{
    public NETJsonFormatter() { }

    static bool Initialize()
    {
        NetJSON.NetJSON.IncludeFields = true;
        NetJSON.NetJSON.IncludeTypeInformation = true;
        return true;
    }

    static bool Initialized = Initialize();

    public byte[] Serialize<T>(T value)
    {
        string aaa = NetJSON.NetJSON.Serialize<T>(value, new NetJSON.NetJSONSettings() { UseEnumString = true });
        return Encoding.UTF8.GetBytes(aaa);
    }

    public T Deserialize<T>(byte[] bytes)
    {
        return NetJSON.NetJSON.Deserialize<T>(Encoding.UTF8.GetString(bytes), new NetJSON.NetJSONSettings() { UseEnumString = true });
    }

    public object Deserialize(Stream serializationStream)
    {
        return NetJSON.NetJSON.Deserialize(typeof(object), Encoding.UTF8.GetString(serializationStream.ToByteArray()));
    }

    public void Serialize(Stream serializationStream, object graph)
    {
        byte[] serialized = Encoding.UTF8.GetBytes(NetJSON.NetJSON.Serialize(graph.GetType(), graph));
        serializationStream.Write(serialized, 0, serialized.Length);
    }
}

the variable 'aaa' has value "".

Thanks!

rpgmaker commented 7 years ago

That is interesting. I guess you should use the first option for now. I will try to play with the second option and see what is causing it to not work.

Thanks,

LorenzoRuggeri commented 7 years ago

If it could help, since I've implemented your NETJson (one year ago, if i remember correctly) this Serialize overload never worked. This is why I ever used other Serialized methods. Just to say it's not something caused by recent rework from scratch. If it could help...

Thanks again!

rpgmaker commented 7 years ago

So the one that takes the Type parameter is bad? I will test it.

Thanks,

rpgmaker commented 7 years ago

I will review this weekend.

Thanks,

rpgmaker commented 7 years ago

Hi,

Can you give me the exact payload/class and method that you called to get the invalid json exception? I am using the other Serialize overload and not able to get the same error you are getting.

Thanks,

LorenzoRuggeri commented 7 years ago

I've tested it over the last hour. Taken away from main project and rebuilt an Unit Test with only class and payload and... I'm so sorry, my fault! A wrapper around your NetJson using Object as generic type caused the problem. Closed thread!

And thanks again for the time spent!

rpgmaker commented 7 years ago

That is fine. I am glad to help.

Thanks,