Wjybxx.Dson.Codec 2.1.0-rc1

This is a prerelease version of Wjybxx.Dson.Codec.
dotnet add package Wjybxx.Dson.Codec --version 2.1.0-rc1
NuGet\Install-Package Wjybxx.Dson.Codec -Version 2.1.0-rc1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Wjybxx.Dson.Codec" Version="2.1.0-rc1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Wjybxx.Dson.Codec --version 2.1.0-rc1
#r "nuget: Wjybxx.Dson.Codec, 2.1.0-rc1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Wjybxx.Dson.Codec as a Cake Addin
#addin nuget:?package=Wjybxx.Dson.Codec&version=2.1.0-rc1&prerelease

// Install Wjybxx.Dson.Codec as a Cake Tool
#tool nuget:?package=Wjybxx.Dson.Codec&version=2.1.0-rc1&prerelease

Dson序列化

Dson有许多强大的特性,你如果只是简单使用Dson,那和普通的序列化组件差不多,可能还没那么方便,因为还要做点准备工作; 如果与Dson深度集成,Dson将提供许多强大的功能。

ps: Readme文档暂时复制了Java的内容,Csharp的序列化将包含Java的所有功能, 传送门Java-Code

特性一览

  1. 支持泛型
  2. 默认值可选写入
  3. 指定数字字段的编码格式(apt)
  4. 支持多态解析,指定指定默认解码类型(apt)
  5. 字段级别的读写代理(核心功能)(apt)
  6. 序列化钩子方法(apt)
  7. 单例支持(apt)
  8. 为外部库类生成Codec(apt)
  9. 外部静态代理(apt)

目前仅支持了Codec的核心编解码功能,可通过手写Codec实现对象的编解码, APT相关功能将由Dson-Apt模块实现。

有限泛型支持

Csharp是真实泛型,为方便使用,Dson库对泛型支持了完整支持 —— 使用上有点配置工作量。
支持泛型的优点:

  1. 类型自解释,精准编解码
  2. 跨语言通信支持 -- 更多是共享配置文件。

注意:虽然C#库提供了完整的泛型支持,在涉及公共文件时,应当限制泛型的使用 —— 避免泛型参数为泛型, 否则影响跨语言时的兼容性。

默认值可选写入

对于基础类型 int32,int64,float,double,bool,可以通过Options.appendDef控制是否写入写入默认值; 对于引用类型,可以通过Options.appendNull控制是否写入null值。

如果数据结构中有大量的可选属性(默认值),那么不写入默认只和null可以很好的压缩数据包。

指定数字字段的编码格式

Dson集成了Protobuf的组件,支持数字的varintunitsintfixed4种编码格式,你可以简单的通过DsonProperty注解声明 字段的编码格式,而且修改编码格式不会导致兼容性问题,eg:

    @DsonProperty(wireType = WireType.Uint)
    public int age;
    
    // 生成的编码代码
    writer.writeInt(names_age, instance.age, WireType.UINT);
    writer.writeString(names_name, instance.name);

示例中的int类型的age字段,在编码时将使用uint格式编码。

指定多态字段的实现

以字典的解码为例,一般序列化框架只能反序列化为Dictionary<K,V>,限制了业务对数据结构的引用;但Dson支持你指定字段的实现类,eg:

    @DsonProperty(impl = EnumMap.class)
    public IDictionary<Sex, String> sex2NameMap3;

上面的这个Map字段在解码时就会解码为EnumMap。具体类型的集合和Map,通常不需要指定实现类,但也是可以指定的,eg:

    // 未指定实现类,APT判断为具体类型,直接调用构造函数
    public Int2IntOpenHashMap currencyMap1;
    
    // 指定了实现类型,APT调用指定类的构造函数
    @DsonProperty(Int2IntOpenHashMap.class)
    public Int2IntMap currencyMap2;

上面的这两个Map字段都会解码为 Int2IntOpenHashMap,编解码代码都是生成的静态代码,看看生成的代码你就很容易明白这是如何实现的。

字段级别的读写代理(核心)

上面都是FieldImpl的简单用法,FieldImpl的最强大之处就在于字段级别的读写代理。
Dson的理念是:能托管的逻辑就让生成的代码负责,用户只处理特殊编解码的部分
一个很好的编码指导是:我们写的代码越少,代码就越干净了,维护成本就越低,项目代码质量就越有保证

与一般的序列化工具不同,Dson支持生成的代码调用用户的自定义代码,从而实现在编解码过程中用户只处理特殊字段逻辑。
举个栗子,假设一个Class有100个字段,有一个字段需要特殊解码,那么用户就可以只处理这一个字段的编解码,其余的仍然由生成的代码负责, 生成的代码在编解码该特殊字段的时候就会调用用户手写的代码。看段代码:

ps: 字段读写代理几乎可实现DsonProperty提供的其它所有功能。

    @DsonProperty(writeProxy = "writeCustom", readProxy = "readCustom")
    public Object custom;

    // 定义了钩子方法后,生成的Codec代码会自动调用
    public void writeCustom(DsonLiteObjectWriter writer, String name) {
        writer.writeObject(custom, TypeArgInfo.OBJECT);
    }

    public void readCustom(DsonLiteObjectReader reader, String name) {
        this.custom = reader.readObject(TypeArgInfo.OBJECT);
    }

我们在类中有一个Object类型的custom字段,并且通过FieldImpl声明了读写代理方法的名字, 生成的代码就会在编解码custom的时候调用用户的方法,下面是生成的代码节选:

    // 解码方法
    instance.currencyMap1 = reader.readObject(names_currencyMap1, CodecBeanExampleSchema.currencyMap1);
    instance.currencyMap2 = reader.readObject(names_currencyMap2, CodecBeanExampleSchema.currencyMap2);
    instance.readCustom(reader, names_custom);
    // 编码方法
    writer.writeObject(names_currencyMap1, instance.currencyMap1, CodecBeanExampleSchema.currencyMap1);
    writer.writeObject(names_currencyMap2, instance.currencyMap2, CodecBeanExampleSchema.currencyMap2);
    instance.writeCustom(writer, names_custom);

序列化钩子方法

Dson提供了writeObjectreadObjectconstructorafterDecodebeforeEncode5种默认的钩子调用支持。

  1. 如果用户定义了包含指定writer的writeObject方法,在编码时将自动调用该方法。
  2. 如果用户定义了包含指定reader的readObject方法,在解码时将自动调用
  3. 如果用户定义了包含指定reader的构造方法,在解码时将自动调用 - 通常用于读取final字段。
  4. 如果用户定义了包含options的afterDecode方法,在解码的末尾将自动调用 - 通常用于处理缓存字段。
  5. 如果用户定义了包含options的beforeEncode方法,在编码之前将自动钓鱼 - 通常用于处理缓存字段。

注意,这里仍然遵从前面的编码指导,你只需要处理特殊的字段,其它字段交给生成的代码处理即可。

    // 序列化前钩子
    public void beforeEncode(ConverterOptions options) {
    }
    // 自定义写入字段 - 紧随beforeEncode调用
    public void writeObject(DsonObjectWriter writer) {
    }
    // 读自定义写入字段
    public void readObject(DsonObjectReader reader) {
    }
    // 反序列化钩子
    public void afterDecode(ConverterOptions options) {
        if (age < 1) throw new IllegalStateException();
    }
   
    // 字段读写钩子
    public void writeCustom(DsonObjectWriter writer, String name) {
    }
    public void readCustom(DsonObjectReader reader, String name) {
    }

单例支持

Dson在ClassImpl注解中提供了singleton属性,当用户指定singleton属性时,生成的Codec将简单调用给定方法返回共享实例。


@ClassImpl(singleton = "getInstance")
@DsonSerializable
public class SingletonTest {
    private static final SingletonTest INST = new SingletonTest("wjybxx", 29);

    public static SingletonTest getInstance() {
        return INST;
    }
}

为外部类生成Codec类

APT除了支持为项目中的类生成Codec外,还支持为外部库的类生成Codec,通过CodecLinkerGroupCodecLinker两个注解实现。


@CodecLinkerGroup(outputPackage = "cn.wjybxx.btree.fsm")
private static class FsmLinker {
    @CodecLinker(classImpl = @ClassImpl)
    private ChangeStateTask<?> changeStateTask;
    @CodecLinker(classImpl = @ClassImpl)
    private StateMachineTask<?> stateMachineTask;
}

ps: 该注解的最佳实例可见BTree-Codec

外部静态代理

如果我们要序列化的是一个外部库的类,且期望能够参与到目标类型序列化过程中,我们就可以通过CodecLinkerBean 实现外部静态代理。
CodecLinkerBean支持除构造函数以外的所有钩子,包括字段的读写代理。

@CodecLinkerBean(value = ThirdPartyBean2.class)
public class CodecLinkerBeanTest {

    @DsonProperty(wireType = WireType.UINT)
    private ThirdPartyBean2 age;

    @DsonProperty(stringStyle = StringStyle.AUTO_QUOTE)
    private ThirdPartyBean2 name;

    // 这些钩子方法,生成的代码会自动调用
    public static void beforeEncode(ThirdPartyBean2 inst, ConverterOptions options) {
    }
    public static void writeObject(ThirdPartyBean2 inst, DsonObjectWriter writer) {
    }
    public static void readObject(ThirdPartyBean2 inst, DsonObjectReader reader) {
    }
    public static void afterDecode(ThirdPartyBean2 inst, ConverterOptions options) {
    }
}

ps:

  1. CodecLinkerBean同样会为目标Bean生成Codec类。
  2. 你可以将CodecLinkerBean看做目标Bean的外部配置类。
  3. 注解的使用实例可参考BigCat项目。

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.1.0-rc1 30 5/20/2024
2.1.0-alpha 38 5/15/2024