protobuf-net AddField忽略IgnoreListHandling

时间:2017-03-04 19:55:24

标签: c# protobuf-net

我有这个数据结构声明:

zero

我通过以下代码手动将字段注册到[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>(); 标记NotACollection。我试图通过提供IgnoreListHandling来强制AddField忽略NotACollection看起来像集合的事实。

尽管如此,我itemType: null, defaultType: null不为空,member.ItemType也不为空。 member.DefaultType已成为生成的some_objects中的repeated字段:

proto

我希望message NotACollectionHolder { repeated int32 some_objects = 1; } 看起来像这样:

proto

我怎样才能实现这一目标?我究竟做错了什么?我如何强制protobuf-net像非收集字段一样处理这个字段?

提前致谢。

2 个答案:

答案 0 :(得分:0)

我认为这可能是RuntimeTypeModel API的错误或限制。

确定ValueMember是否为集合的方法是RuntimeTypeModel.ResolveListTypes()。当传入的itemType为空时,它会尝试推断集合项类型。仅使用静态属性为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是只读的,而MetaType[int fieldNumber]只是get-only,所以似乎没有一个简单的API可以调用来解决这个问题。您可以考虑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];
}

答案 1 :(得分:0)

我发现这确实是protobuf-net中的一个错误。修复它的第一种方法是更改​​protobuf-net的源代码:在函数文件MetaType.cs中

if (model.FindWithoutAdd(miType)?.IgnoreListHandling == true)
{
    itemType = null;
    defaultType = null;
}
else
    ResolveListTypes(model, miType, ref itemType, ref defaultType);

替换行

itemType

defaultType

另一种方法是在添加字段后使用反射将对象ValueMember的私有字段nullValueMember 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); 设置为sqoop import --connect jdbc:mysql://localhost:3306/hadoopguide --table widgets --username <username> --password <password> --split-by id -m 1 --hive-import;

sqoop create-hive-table --connect jdbc:mysql://localhost:3306/hadoopguide --table widgets --username <username> --password <password> --fields-terminated-by ',';

希望这会对某人有所帮助。