Giter Club home page Giter Club logo

Comments (8)

neuecc avatar neuecc commented on June 16, 2024

Thank you.

Hmn, is this concept match for you?

public interface IDynamicRoot
{
}

[ZeroFormattable]
public class TypeA : IDynamicRoot {}

[ZeroFormattable]
public class TypeB : IDynamicRoot{}

// RegisterDynamic? : key as byte(binary size)
Formatter.RegisterDynamicUnion<IDynamicRoot, byte>(x =>
{
    x.Register(key: (byte)0, subType: typeof(TypeA));
    x.Register(key: (byte)1, subType: typeof(TypeB));
});

// RegisterDynamic? : key as string(convenient but use many binary spaces)
Formatter.RegisterDynamicUnion<IDynamicRoot, string>(x =>
{
    // Scan rules can be freely built(manual? assembly scan +attribute? etc...).
    x.Register(key: typeof(TypeA).FullName, subType: typeof(TypeA));
    x.Register(key: nameof(TypeB).FullName, subType: typeof(TypeB));
});

// Serialize/Deserialize...
var bin = ZeroFormatterSerializer.Serialize<IDynamicRoot>(new TypeA());

// o is TypeA
var o = ZeroFormatterSerializer.Deserialize<IDynamicRoot>(bin);

If removing the interface root, is this another api suggestion good?(I think this is better)

[ZeroFormattable]
public class TypeA {}

[ZeroFormattable]
public class TypeB {}

Formatter.RegisterDynamicUnion<byte, string>(x =>
{
    x.Register(key: (byte)0, subType: typeof(TypeA));
    x.Register(key: (byte)1, subType: typeof(TypeB));

    return "DynamicUnionName_A"; // key of the dynamic union serializer
});

var bin = ZeroFormatterSerializer.SerializeDynamicUnion("DynamicUnionName_A", new TypeA());

// object(TypeA)
var o = ZeroFormatterSerializer.DeserializeDynamicUnion("DynamicUnionName_A", bin);

from zeroformatter.

Rurouni avatar Rurouni commented on June 16, 2024

Ideally that's what I was thinking about

//consumer types
public class MessageBase {}
public interface IEvent{}

public class UnknownEvent : IEvent {}
[ZeroFormattable]
public class EventA : IEvent {}
[ZeroFormattable]
public class EventB : IEvent {}

public class UnknownMessage : MessageBase {}
[ZeroFormattable]
public class MessageA : MessageBase {}
[ZeroFormattable]
public class MessageB : MessageBase 
{
    [Index(0)]
    public virtual List<IEvent> Events { get; set; }
}

[ZeroFormattable]
public class MessageBatch : MessageBase 
{
    [Index(0)]
    public virtual List<MessageBase> Messages { get; set; }
}

//serialiser registration
var serialisationModel = ZeroFormatterSerializer.CreateDefaultModel()

serialisationModel.RegisterTypesWithAttributes(someAssembly);

serialisationModel.RegisterPolymorphicType<IEvent, int, UnknownEvent>(x =>
{
    //can be easily extended to reflection based scan if library consumer wants it
    x.Register(key: SomeIdGen(nameof(EventA)), subType: typeof(EventA));
    x.Register(key: SomeIdGen(nameof(EventB)), subType: typeof(EventB));
});

serialisationModel.RegisterPolymorphicType<MessageBase, int, UnknownMessage>(x =>
{
    x.Register(key: SomeIdGen(nameof(MessageA)), subType: typeof(MessageA));
    x.Register(key: SomeIdGen(nameof(MessageB)), subType: typeof(MessageB));
    x.Register(key: SomeIdGen(nameof(MessageBatch)), subType: typeof(MessageBatch));
});

MessageBase msg  = new MessageBatch { Messages = new List<MessageBase> { new MessageB { Events = new List<IEvent> { new EventA() }}};

var serialiser  = ZeroFormatterSerializer.CreateFromModel(serialisationModel);
var bts = serialiser.Serialise(msg);
var message = serialiser.Deserialise<MessageBase>(bts)

ZeroFormatterProtocolGenerator.CompileFromModel(serialisationModel, "path\UnityCompatibleProtocol.dll");

The reason for UnknownMessage/UnknownEvent is backward compatibility, so that when you read List in older client instead of failing on deserialisation it can just fallback to set them as UnknownMessage allowing layer above to skip new unknown messages without losing known ones.

The reason for non-static serialiser class is so you can have differently configured serialisers in same AppDomain. It's generally better to have non-static implementation that is exposed in preconfigured static wrapper class, because you can always create a user friendly singleton from non-static class but not other way around.

Also because protocol is generated at runtime we need to be able to generate precompiled dll for unity from it, so in addition to command line tool I suggest exposing protocol generator as part of library

I really like the library btw, if it wasn't for the protocol like above I would have already switched from Protobuf-Net to ZeroFormatter

from zeroformatter.

neuecc avatar neuecc commented on June 16, 2024

Thank you, I understood.
Based on your suggestion, I thought about a new API.

public interface ITypeResolver
{
    // can be precompiled for improve initial performance(or AOT environment).
    // precompiled generator always set to false.
    bool IsUseDefaultDynamicSerializer { get; }

    // AppendFormatterResolver in normal.
    object ResolveFormatter(Type type);

    // RegisterPolymorphicType
    object ResolveDynamicUnion(Type unionType, DynamicUnionResolver resolver);
}

public class CustomSerializationModel : ITypeResolver
{
    public bool IsUseDefaultDynamicSerializer
    {
        get
        {
            return true;
        }
    }

    public object ResolveFormatter(Type t)
    {
        return null;
    }

    public void ResolveDynamicUnion(Type unionType, DynamicUnionResolver resolver)
    {
        if (unionType == typeof(IEvent))
        {
            // resolver api is [Type, object] for reflection based regsiter(but not typesafe)
            resolver.RegisterDiscriminatedKeyType(typeof(int));
            resolver.RegisterFallbackType(typeof(UnknownEvent));
            resolver.RegisterSubType(key: SomeIdGen(nameof(EventA)), subType: typeof(EventA));
        }
        // else if...
    }
}

// chnage Formatter<T> to here
    public abstract class Formatter<TTypeResolver, T>
        where TTypeResolver : ITypeResolver, new()
// usage

// mark DynamicUnion on base type...
[DynamicUnion]
public class MessageBase {}
[DynamicUnion]
public interface IEvent{}

/// use dynamic serializer
ZeroFormatterSerializer.CustomSerializer<CustomSerializationModel>.Serialize();

// create dll
// ZeroFormatterProtocolGenerator.CompileDefault();
// ZeroFormatterProtocolGenerator.Compile<CustomSerializationModel>(IEnumerable<Type> generateTypes,string typeFullName, string path);
// ZeroFormatterProtocolGenerator.Compile<CustomSerializationModel>(IEnumerable<Assembly> scanAssemblies,string typeFullName, string path);

// use dll
// ZeroFormatterSerializer.SetDefault<GeneratedSerializationModel>();
// ZeroFormatterSerializer.Serialize();

Serializer acquisition by static type is the core of ZeroFormatter and can not be changed.
However, it can be solved by static type with custom type.

I hope these changes will satisfy your requirements, and I want you to switch:)

from zeroformatter.

Rurouni avatar Rurouni commented on June 16, 2024

That looks good.
My only question is around performance with CustomSerializationModel... to make it fast reflection based scan should happen only once and mapping of type->formatter should be cached otherwise ZeroFormatterSerializer.CustomSerializer<CustomSerializationModel>().Serialize(); will be prohibitively expensive.
Or is you plan that consumer just saves instance of serialiser :

//once on startup
var serialiser = ZeroFormatterSerializer.CustomSerializer<CustomSerializationModel>();

//many times, threadsafe
serialiser.Serialise();

Cheers

from zeroformatter.

neuecc avatar neuecc commented on June 16, 2024

ZeroFormatterSerializer.CustomSerializer<CustomSerializationModel>.Serialize<T>(T value)
calls
Formatter<CustomSerializationModel, T>.Default.Serialize(T value);
so
Formatter<CustomSerializationModel, T>.Default is automatically cached on static field.

from zeroformatter.

Rurouni avatar Rurouni commented on June 16, 2024

Understood, but how the caching for void ResolveDynamicUnion(Type unionType, DynamicUnionResolver resolver) will be working? As read through cache on first time you serialise something that contains IEvent(for example)?

from zeroformatter.

neuecc avatar neuecc commented on June 16, 2024

Yes, that cache will be done for the first time.
This is the flow at the time of caching.

CustomSerializer<CustomSerializationModel>.Serialize<IEvent>()
->
Formatter<CustomSerializationModel, IEvent>.Default
->
ResolveDynamicUnion(typoef(IEvent), resolver)
->
Formatter<CustomSerializationModel, IEvent>.Default = resolver.CreateFormatter() // cached!

from zeroformatter.

neuecc avatar neuecc commented on June 16, 2024

Today I released 1.5.0 and it supports DynamicUnion.
Currently supports ITypeResolver configuraiton, RegisterDynamic, Fallback type on Union.
DLL code generate is not yet.

from zeroformatter.

Related Issues (20)

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.