protobuf-net AddField 忽略 IgnoreListHandling
protobuf-net AddField ignores IgnoreListHandling
我有这个数据结构声明:
[ProtoContract]
public class NotACollectionHolder
{
public NotACollection some_objects;
}
[ProtoContract(IgnoreListHandling = true, ImplicitFields = ImplicitFields.AllPublic)]
public class NotACollection : IEnumerable<int>
{
public int some_data;
// something looks like a collection API
public void Add(int a) { }
public IEnumerator<int> GetEnumerator() { throw new NotImplementedException(); }
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
}
我正在通过以下代码手动将字段注册到 MetaType
:
MetaType meta = RuntimeTypeModel.Default.Add(typeof(NotACollectionHolder), false);
ValueMember member = meta.AddField(1, "some_objects", itemType: null, defaultType: null);
string proto = Serializer.GetProto<NotACollectionHolder>();
我用IgnoreListHandling
标记NotACollection
。我试图通过提供 itemType: null, defaultType: null
.
来强制 AddField
忽略 NotACollection
看起来像集合的事实
然而,我有 member.ItemType
is not null,并且 member.DefaultType
is not null either。 some_objects
成为生成的 proto
:
中的一个 repeated
字段
message NotACollectionHolder {
repeated int32 some_objects = 1;
}
我希望 proto
看起来像这样:
message NotACollection {
optional int32 some_data = 1 [default = 0];
}
message NotACollectionHolder {
optional NotACollection some_objects = 1;
}
我怎样才能做到这一点?我究竟做错了什么?如何强制 protobuf-net 将此字段视为非集合字段?
提前致谢。
我认为这可能是 RuntimeTypeModel
API 的错误或限制。
判断是否为ValueMember
is a collection is RuntimeTypeModel.ResolveListTypes()
的方法。当传入的 itemType
为 null 时,它会尝试推断集合项类型。仅使用静态属性为 NotACollectionHolder
构建合约时,例如通过执行以下操作:
var model = TypeModel.Create();
var schema = model.GetSchema(typeof(NotACollectionHolder));
然后调用 ProtoBuf.Meta.MetaType.ApplyDefaultBehaviour(bool isEnum, ProtoMemberAttribute normalizedAttribute)
来创建初始化 ValueMember
。它具有以下逻辑:
// check for list types
ResolveListTypes(model, effectiveType, ref itemType, ref defaultType);
// but take it back if it is explicitly excluded
if(itemType != null)
{ // looks like a list, but double check for IgnoreListHandling
int idx = model.FindOrAddAuto(effectiveType, false, true, false);
if(idx >= 0 && model[effectiveType].IgnoreListHandling)
{
itemType = null;
defaultType = null;
}
}
注意到 IgnoreListHandling
的显式检查了吗?这正确地防止了 some_objects
被序列化为一个集合。
相反,如果以编程方式添加 ValueMember
,如下所示:
var model = TypeModel.Create();
var meta = model.Add(typeof(NotACollectionHolder), false);
var member = meta.AddField(1, "some_objects", null, null);
var schema = model.GetSchema(typeof(NotACollectionHolder));
然后 MetaType.AddField(int fieldNumber, string memberName, Type itemType, Type defaultType, object defaultValue)
被调用,它只是做:
ResolveListTypes(model, miType, ref itemType, ref defaultType);
请注意没有检查 IgnoreListHandling
?这是你的问题的原因。
不幸的是,ValueType.itemType
is read-only and MetaType[int fieldNumber]
is get-only so there doesn't seem to be a simple API to call to fix this. You might consider reporting an issue。
我能找到的唯一解决方法是为 NotACollectionHolder
类型引入一个代理项,如下所示:
[ProtoContract]
internal class NotACollectionHolderSurrogate
{
[ProtoMember(1)]
internal NotACollectionSurrogate some_objects;
public static implicit operator NotACollectionHolder(NotACollectionHolderSurrogate input)
{
if (input == null)
return null;
return new NotACollectionHolder { some_objects = input.some_objects };
}
public static implicit operator NotACollectionHolderSurrogate(NotACollectionHolder input)
{
if (input == null)
return null;
return new NotACollectionHolderSurrogate { some_objects = input.some_objects };
}
}
[ProtoContract]
internal class NotACollectionSurrogate
{
[ProtoMember(1)]
public int some_data;
public static implicit operator NotACollection(NotACollectionSurrogate input)
{
if (input == null)
return null;
return new NotACollection { some_data = input.some_data };
}
public static implicit operator NotACollectionSurrogate(NotACollection input)
{
if (input == null)
return null;
return new NotACollectionSurrogate { some_data = input.some_data };
}
}
然后做:
var model = TypeModel.Create();
model.Add(typeof(NotACollectionHolder), false).SetSurrogate(typeof(NotACollectionHolderSurrogate));
var schema = model.GetSchema(typeof(NotACollectionHolder));
按要求生成的合约:
message NotACollectionHolderSurrogate {
optional NotACollectionSurrogate some_objects = 1;
}
message NotACollectionSurrogate {
optional int32 some_data = 1 [default = 0];
}
我发现这确实是protobuf-net的一个bug。修复它的第一种方法是更改 protobuf-net 的源代码:在文件 MetaType.cs 中在函数
中
private ValueMember AddField(int fieldNumber, string memberName, Type itemType, Type defaultType, object defaultValue)
替换行
ResolveListTypes(model, miType, ref itemType, ref defaultType);
和
if (model.FindWithoutAdd(miType)?.IgnoreListHandling == true)
{
itemType = null;
defaultType = null;
}
else
ResolveListTypes(model, miType, ref itemType, ref defaultType);
另一种方法是使用反射将对象ValueMember
的私有字段itemType
和defaultType
设置为添加字段后的null
:
ValueMember m = meta.AddField(++last_field_number, f.Name, f.ItemType, f.DefaultType);
m.GetType().GetField("itemType", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(m, null);
m.GetType().GetField("defaultType", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(m, null);
希望这对某人有所帮助。
我有这个数据结构声明:
[ProtoContract]
public class NotACollectionHolder
{
public NotACollection some_objects;
}
[ProtoContract(IgnoreListHandling = true, ImplicitFields = ImplicitFields.AllPublic)]
public class NotACollection : IEnumerable<int>
{
public int some_data;
// something looks like a collection API
public void Add(int a) { }
public IEnumerator<int> GetEnumerator() { throw new NotImplementedException(); }
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
}
我正在通过以下代码手动将字段注册到 MetaType
:
MetaType meta = RuntimeTypeModel.Default.Add(typeof(NotACollectionHolder), false);
ValueMember member = meta.AddField(1, "some_objects", itemType: null, defaultType: null);
string proto = Serializer.GetProto<NotACollectionHolder>();
我用IgnoreListHandling
标记NotACollection
。我试图通过提供 itemType: null, defaultType: null
.
AddField
忽略 NotACollection
看起来像集合的事实
然而,我有 member.ItemType
is not null,并且 member.DefaultType
is not null either。 some_objects
成为生成的 proto
:
repeated
字段
message NotACollectionHolder {
repeated int32 some_objects = 1;
}
我希望 proto
看起来像这样:
message NotACollection {
optional int32 some_data = 1 [default = 0];
}
message NotACollectionHolder {
optional NotACollection some_objects = 1;
}
我怎样才能做到这一点?我究竟做错了什么?如何强制 protobuf-net 将此字段视为非集合字段?
提前致谢。
我认为这可能是 RuntimeTypeModel
API 的错误或限制。
判断是否为ValueMember
is a collection is RuntimeTypeModel.ResolveListTypes()
的方法。当传入的 itemType
为 null 时,它会尝试推断集合项类型。仅使用静态属性为 NotACollectionHolder
构建合约时,例如通过执行以下操作:
var model = TypeModel.Create();
var schema = model.GetSchema(typeof(NotACollectionHolder));
然后调用 ProtoBuf.Meta.MetaType.ApplyDefaultBehaviour(bool isEnum, ProtoMemberAttribute normalizedAttribute)
来创建初始化 ValueMember
。它具有以下逻辑:
// check for list types
ResolveListTypes(model, effectiveType, ref itemType, ref defaultType);
// but take it back if it is explicitly excluded
if(itemType != null)
{ // looks like a list, but double check for IgnoreListHandling
int idx = model.FindOrAddAuto(effectiveType, false, true, false);
if(idx >= 0 && model[effectiveType].IgnoreListHandling)
{
itemType = null;
defaultType = null;
}
}
注意到 IgnoreListHandling
的显式检查了吗?这正确地防止了 some_objects
被序列化为一个集合。
相反,如果以编程方式添加 ValueMember
,如下所示:
var model = TypeModel.Create();
var meta = model.Add(typeof(NotACollectionHolder), false);
var member = meta.AddField(1, "some_objects", null, null);
var schema = model.GetSchema(typeof(NotACollectionHolder));
然后 MetaType.AddField(int fieldNumber, string memberName, Type itemType, Type defaultType, object defaultValue)
被调用,它只是做:
ResolveListTypes(model, miType, ref itemType, ref defaultType);
请注意没有检查 IgnoreListHandling
?这是你的问题的原因。
不幸的是,ValueType.itemType
is read-only and MetaType[int fieldNumber]
is get-only so there doesn't seem to be a simple API to call to fix this. You might consider reporting an issue。
我能找到的唯一解决方法是为 NotACollectionHolder
类型引入一个代理项,如下所示:
[ProtoContract]
internal class NotACollectionHolderSurrogate
{
[ProtoMember(1)]
internal NotACollectionSurrogate some_objects;
public static implicit operator NotACollectionHolder(NotACollectionHolderSurrogate input)
{
if (input == null)
return null;
return new NotACollectionHolder { some_objects = input.some_objects };
}
public static implicit operator NotACollectionHolderSurrogate(NotACollectionHolder input)
{
if (input == null)
return null;
return new NotACollectionHolderSurrogate { some_objects = input.some_objects };
}
}
[ProtoContract]
internal class NotACollectionSurrogate
{
[ProtoMember(1)]
public int some_data;
public static implicit operator NotACollection(NotACollectionSurrogate input)
{
if (input == null)
return null;
return new NotACollection { some_data = input.some_data };
}
public static implicit operator NotACollectionSurrogate(NotACollection input)
{
if (input == null)
return null;
return new NotACollectionSurrogate { some_data = input.some_data };
}
}
然后做:
var model = TypeModel.Create();
model.Add(typeof(NotACollectionHolder), false).SetSurrogate(typeof(NotACollectionHolderSurrogate));
var schema = model.GetSchema(typeof(NotACollectionHolder));
按要求生成的合约:
message NotACollectionHolderSurrogate {
optional NotACollectionSurrogate some_objects = 1;
}
message NotACollectionSurrogate {
optional int32 some_data = 1 [default = 0];
}
我发现这确实是protobuf-net的一个bug。修复它的第一种方法是更改 protobuf-net 的源代码:在文件 MetaType.cs 中在函数
中private ValueMember AddField(int fieldNumber, string memberName, Type itemType, Type defaultType, object defaultValue)
替换行
ResolveListTypes(model, miType, ref itemType, ref defaultType);
和
if (model.FindWithoutAdd(miType)?.IgnoreListHandling == true)
{
itemType = null;
defaultType = null;
}
else
ResolveListTypes(model, miType, ref itemType, ref defaultType);
另一种方法是使用反射将对象ValueMember
的私有字段itemType
和defaultType
设置为添加字段后的null
:
ValueMember m = meta.AddField(++last_field_number, f.Name, f.ItemType, f.DefaultType);
m.GetType().GetField("itemType", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(m, null);
m.GetType().GetField("defaultType", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(m, null);
希望这对某人有所帮助。