Сериализация с ProtoBuf.NET без пометки членов - PullRequest
14 голосов
/ 30 сентября 2011

Я где-то читал комментарий автора ProtoBuf.NET о том, что:

Есть варианты автоматического вывода чисел, но это хрупко и не рекомендуется.Используйте это, только если вы знаете, что вам никогда не нужно добавлять больше участников (они упорядочивают их по алфавиту, поэтому добавление нового AardvarkCount разрушит все).

Это именно та ситуация, в которой я заинтересован:)

У меня есть кое-что, похожее на сценарий сокращения карт, где я хочу сериализовать результаты, генерируемые на удаленных машинах, с использованием буферов протокола (например, сторона карты map-уменьшает), а затем прочитать их и объединитьэти результаты для дальнейшей обработки (например, сторона «Reduce»).

Я не хочу начинать марафон оформления атрибутов для каждого возможного класса, который у меня может быть сериализован во время этого процесса, и я нахожубуферы протокола должны быть очень заманчивыми, так как я могу создавать результаты с Mono и без труда использовать их в MS.NET и наоборот ...

Очевидные недостатки, связанные с отсутствием предварительной пометки участников, меня не беспокоятточно такая же ревизия программного обеспечения делает генерацию / потреблениеn, так что мне не нужно беспокоиться о новых членах poppiПоднимаясь в коде и портя всю мою схему ...

Короче говоря, мой вопрос:

  • Как мне это сделать (Сериализация с ProtoBuf.NET без тегов /создание мета-классов самостоятельно)?
  • Есть ли в моей схеме дыра, которую я явно упустил?

Ответы [ 2 ]

9 голосов
/ 30 сентября 2011

Если вы можете жить с атрибутом single , то хитрость заключается в следующем:

    [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
    public class WithImplicitFields
    {
        public int X { get; set; }
        public string Y { get; set; }
    }

здесь есть 2 варианта;AllPublic работает как XmlSerializer - общедоступные свойства и поля сериализуются (используя алфавитный порядок для выбора номеров тегов);AllFields работает примерно так же, как BinaryFormatter - поля сериализуются (опять же, в алфавитном порядке).

Я не могу вспомнить, доступно ли это еще в API v2;Я знаю, что это в моем списке вещей, чтобы обеспечить работу!Но если вы хотите его в v2 без каких-либо атрибутов , я уверен, что могу добавить перегрузку Add(ImplicitFields).

Пока 2 конца не выходят из строя, этохорошо.Если вы храните данные или не выполняете версию двух концов «в шаге», тогда могут возникнуть проблемы.См. Также комментарии intellisense к перечислению (которое в значительной степени повторяет предупреждение, о котором вы уже знаете).

1 голос
/ 21 сентября 2015

У меня была такая же проблема, и я решил ее с помощью TypeModel.Он основан на свойствах, упорядоченных по их имени (однако он не проверяет существование установщика / получателя свойства или возможность сериализации данного типа):

[TestFixture]
public class InferredProtoPoc
{
    [Test]
    public void UsageTest()
    {
        var model = TypeModel.Create();
        // Dynamically create the model for MyPoco
        AddProperties(model, typeof(MyPoco));
        // Display the Generated Schema of MyPoco
        Console.WriteLine(model.GetSchema(typeof(MyPoco)));

        var instance = new MyPoco
        {
            IntegerProperty = 42,
            StringProperty = "Foobar",
            Containers = new List<EmbeddedPoco>
            {
                new EmbeddedPoco { Id = 12, Name = "MyFirstOne" },
                new EmbeddedPoco { Id = 13, Name = "EmbeddedAgain" }
            }
        };

        var ms = new MemoryStream();
        model.Serialize(ms, instance);
        ms.Seek(0, SeekOrigin.Begin);
        var res = (MyPoco) model.Deserialize(ms, null, typeof(MyPoco));
        Assert.IsNotNull(res);
        Assert.AreEqual(42, res.IntegerProperty);
        Assert.AreEqual("Foobar", res.StringProperty);
        var list = res.Containers;
        Assert.IsNotNull(list);
        Assert.AreEqual(2, list.Count);
        Assert.IsTrue(list.Any(x => x.Id == 12));
        Assert.IsTrue(list.Where(x => x.Id == 12).Any(x => x.Name == "MyFirstOne"));
        Assert.IsTrue(list.Any(x => x.Id == 13));
        Assert.IsTrue(list.Where(x => x.Id == 13).Any(x => x.Name == "EmbeddedAgain"));
    }

    private static void AddProperties(RuntimeTypeModel model, Type type)
    {
        var metaType = model.Add(type, true);
        foreach (var property in type.GetProperties().OrderBy(x => x.Name))
        {
            metaType.Add(property.Name);
            var propertyType = property.PropertyType;
            if (!IsBuiltinType(propertyType) &&
                !model.IsDefined(propertyType) && 
                propertyType.GetProperties().Length > 0)
            {

                AddProperties(model, propertyType);
            }
        }
    }

    private static bool IsBuiltinType(Type type)
    {
        return type.IsValueType || type == typeof (string);
    }
}

public class MyPoco
{
    public int IntegerProperty { get; set; }
    public string StringProperty { get; set; }
    public List<EmbeddedPoco> Containers { get; set; }
}

public class EmbeddedPoco
{
    public int Id { get; set; }
    public String Name { get; set; }
}

И это то, что вы получаете от его запуска.

    message EmbeddedPoco {
       optional int32 Id = 1;
       optional string Name = 2;
    }
    message MyPoco {
       repeated EmbeddedPoco Containers = 1;
       optional int32 IntegerProperty = 2;
       optional string StringProperty = 3;
    }

Для повышения производительности вы можете скомпилировать TypeModel и / или сохранить сгенерированный прото для использования в будущем.Однако помните, что скрытая зависимость от Protocol Buffer может быть опасной в долгосрочной перспективе, если poco (простой старый контейнерный объект) эволюционирует.

...