YamlDotNet - 自定义序列化
YamlDotNet - Custom Serialization
我有一个 .NET class 代表一个 RPC 方法调用,像这样:
class MethodCall
{
public string MethodName { get; set; }
public Collection<object> Arguments { get; set; }
}
我想将 Collection<MethodCall>
序列化为 YAML。我正在使用 YamlDotNet 来实现这一点。
默认情况下,YamlDotNet 将像这样序列化这些对象:
methodName: someName
arguments:
- arg1
- arg2
- ...
我想将生成的 YAML 简化为:
someName:
- arg1
- arg2
有什么简单的方法可以做到这一点吗?请注意,参数可以是复杂的对象(即不是简单的标量)。
您可以通过注册执行所需转换的 IYamlTypeConverter
实现来实现此目的。
这是一个可能的实现:
public sealed class MethodCallConverter : IYamlTypeConverter
{
// Unfortunately the API does not provide those in the ReadYaml and WriteYaml
// methods, so we are forced to set them after creation.
public IValueSerializer ValueSerializer { get; set; }
public IValueDeserializer ValueDeserializer { get; set; }
public bool Accepts(Type type) => type == typeof(MethodCall);
public object ReadYaml(IParser parser, Type type)
{
parser.Consume<MappingStart>();
var call = new MethodCall
{
MethodName = (string)ValueDeserializer.DeserializeValue(parser, typeof(string), new SerializerState(), ValueDeserializer),
Arguments = (Collection<object>)ValueDeserializer.DeserializeValue(parser, typeof(Collection<object>), new SerializerState(), ValueDeserializer),
};
parser.Consume<MappingEnd>();
return call;
}
public void WriteYaml(IEmitter emitter, object value, Type type)
{
emitter.Emit(new MappingStart());
var call = (MethodCall)value;
ValueSerializer.SerializeValue(emitter, call.MethodName, typeof(string));
ValueSerializer.SerializeValue(emitter, call.Arguments, typeof(Collection<object>));
emitter.Emit(new MappingEnd());
}
}
转换器需要通过WithTypeConverter
方法注册到SerializerBuilder
和DeserializerBuilder
中。请注意,YamlDotNet 没有为我们提供递归调用(反)序列化器的方法,因此我们必须设置一些 public 属性作为解决方法。这并不像它应该的那样干净,但仍然有效:
string SerializeMethodCall(MethodCall call)
{
var methodCallConverter = new MethodCallConverter();
var serializerBuilder = new SerializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeConverter(methodCallConverter);
methodCallConverter.ValueSerializer = serializerBuilder.BuildValueSerializer();
var serializer = serializerBuilder.Build();
var yaml = serializer.Serialize(call);
return yaml;
}
MethodCall DeserializeMethodCall(string yaml)
{
var methodCallConverter = new MethodCallConverter();
var deserializerBuilder = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeConverter(methodCallConverter);
methodCallConverter.ValueDeserializer = deserializerBuilder.BuildValueDeserializer();
var deserializer = deserializerBuilder.Build();
var call = deserializer.Deserialize<MethodCall>(yaml);
return call;
}
我有一个 .NET class 代表一个 RPC 方法调用,像这样:
class MethodCall
{
public string MethodName { get; set; }
public Collection<object> Arguments { get; set; }
}
我想将 Collection<MethodCall>
序列化为 YAML。我正在使用 YamlDotNet 来实现这一点。
默认情况下,YamlDotNet 将像这样序列化这些对象:
methodName: someName
arguments:
- arg1
- arg2
- ...
我想将生成的 YAML 简化为:
someName:
- arg1
- arg2
有什么简单的方法可以做到这一点吗?请注意,参数可以是复杂的对象(即不是简单的标量)。
您可以通过注册执行所需转换的 IYamlTypeConverter
实现来实现此目的。
这是一个可能的实现:
public sealed class MethodCallConverter : IYamlTypeConverter
{
// Unfortunately the API does not provide those in the ReadYaml and WriteYaml
// methods, so we are forced to set them after creation.
public IValueSerializer ValueSerializer { get; set; }
public IValueDeserializer ValueDeserializer { get; set; }
public bool Accepts(Type type) => type == typeof(MethodCall);
public object ReadYaml(IParser parser, Type type)
{
parser.Consume<MappingStart>();
var call = new MethodCall
{
MethodName = (string)ValueDeserializer.DeserializeValue(parser, typeof(string), new SerializerState(), ValueDeserializer),
Arguments = (Collection<object>)ValueDeserializer.DeserializeValue(parser, typeof(Collection<object>), new SerializerState(), ValueDeserializer),
};
parser.Consume<MappingEnd>();
return call;
}
public void WriteYaml(IEmitter emitter, object value, Type type)
{
emitter.Emit(new MappingStart());
var call = (MethodCall)value;
ValueSerializer.SerializeValue(emitter, call.MethodName, typeof(string));
ValueSerializer.SerializeValue(emitter, call.Arguments, typeof(Collection<object>));
emitter.Emit(new MappingEnd());
}
}
转换器需要通过WithTypeConverter
方法注册到SerializerBuilder
和DeserializerBuilder
中。请注意,YamlDotNet 没有为我们提供递归调用(反)序列化器的方法,因此我们必须设置一些 public 属性作为解决方法。这并不像它应该的那样干净,但仍然有效:
string SerializeMethodCall(MethodCall call)
{
var methodCallConverter = new MethodCallConverter();
var serializerBuilder = new SerializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeConverter(methodCallConverter);
methodCallConverter.ValueSerializer = serializerBuilder.BuildValueSerializer();
var serializer = serializerBuilder.Build();
var yaml = serializer.Serialize(call);
return yaml;
}
MethodCall DeserializeMethodCall(string yaml)
{
var methodCallConverter = new MethodCallConverter();
var deserializerBuilder = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeConverter(methodCallConverter);
methodCallConverter.ValueDeserializer = deserializerBuilder.BuildValueDeserializer();
var deserializer = deserializerBuilder.Build();
var call = deserializer.Deserialize<MethodCall>(yaml);
return call;
}