9

I'm trying to use ProtoBuf-NET in my project (it's mostly Silverlight 4 project).

I'm having difficulties serializing my Model collections, they all are defined like this:

private List<T> _itemsSet;
public IEnumerable<T> TSet
{
    get {return _itemsSet;}
    set {_itemsSet = value == null ? new List<T>() : new List<T>(value);}
}
public void AddT(T item)
{
    //Do my logic here
    _itemsSet.Add(item);
}

Update: First I can't serialize it - No serializer defined for type: System.Collections.Generic.IEnumerable1[MyType]`. Second I think I will be unable to desirialize it based on manual, and protobuf-net source code analyzes.

  1. Is there a way to extend protobuf-net to supply delegate to external Add method in ProtoMemeber attribute?
  2. Why using ProtoMember(1, OverwriteList=true) doesn't work? Doesn't it suppose to overwrite collection and shouldn't care about Add<T>() method? Why it just don't try to set this property to T[] or List<T> or any set assignable to IEnumerable<T> ?
  3. Is there a way to supply custom reflection mechanism to work with private fields in Silverlight, like: implementing: public interface IReflectable{ object GetValue(FieldInfo field); void SetValue(FieldInfo field, object value); } to work with private fields. I have used such approach to work with private fields with Db4o: http://community.versant.com/Forums/tabid/98/aft/10881/Default.aspx
  4. What options I have except creating inherited MyTypeCollection<T> : Collection<T> ?
Alex Burtsev
  • 12,418
  • 8
  • 60
  • 87

2 Answers2

4
  1. not at current, no; the type exposed must have (at a minimum) an Add method. While I have no objection to investigating the possibility of an Add outside the object itself, this is complicated since you are then looking at a different "primary" object (the sequence vs the container). However; if your parent object implemented IEnumerable<T> (returning _itemsSet.GetEnumerator() etc), then it would find the Add automatically

  2. I can't see the context here; however, I suspect that without the Add it still isn't happy to consider it a list in the first place. I see the way you are going with that, though, and it is perhaps a way it could reason "I can use a List<T> here)

  3. It isn't something I've investigated, to be honest; so: no

  4. The type exposed in the property must, as a minimum: implement IEnumerable (although IEnumerable<T> would be preferred), and expose an Add(T) method. It doesn't have to be Collection<T> / List<T> / etc - simply: it must (at present) have some mechanism to add. IList<T> would be a a pragmatic option, but I gather that isn't quite what you want.

Jonathan is right that a surrogate for the outer-class (the thing which has _itemsSet, TSet and AddT) might also be an option.

If the outer-class only exists to have the collection and the add method, then just adding : IEnumerable<T> and renaming AddT to Add would probably make it work.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • What about my IEnumerableT serialization problem? I have run my code in debug mode with latest protobuf-net source code and there is no Collection | IEnumerable handling code in ValueMember.cs : TryGetCoreSerializer LINE:330 So I get exception at ValueMember.cs line:262. – Alex Burtsev Oct 17 '11 at 13:18
  • 1
    @Alex *by itself*, `IEnumerable` is not serializable, since it can't robustly deserialize it. At best, I can give you a better error message! It *can* (I really, really hope) handle "Collection" - but it needs to have some API *on the exposed type* to `Add` things. – Marc Gravell Oct 17 '11 at 13:25
  • Are you planing to continue to work on protobuf-net and extend it? What is project status? I'm was hoping to use it in real life project, but in it's current state, it's unusable for me, thought it's still better than to write my own solution from scratch. – Alex Burtsev Oct 17 '11 at 13:51
  • @Alex it is in active development and use. Re extending it - first I need to clearly understand the issue, for which a more complete example would help hugely. – Marc Gravell Oct 17 '11 at 18:13
  • All my models expose collections using IEnumerable. It simply means that the underlying collection can only be accessed through that limited interface. While IEnumerable is not serializable, why can't you simply deserialize the contents into a List. That implements IEnumerable but you have full control of the contents. I'm not willing to change the semantics of all my view models simply for serialization purposes, and so this is a disappointing result. – G-Mac Aug 21 '12 at 09:10
  • @G-Mac yes, but you're over-simplifying. In the general case, it is more complex... for example, say it is deserializing, and finds that the property is not null; what should it do then? it needs to add data, but **changing** the implementation is not ideal, and breaks just as scenarios as it fixes. However! You can add a shim property easily enough that would have whatever rules you want. What you describe (automatic inference via `List`) *is* fully supported for `IList` properties, because it can *use the existing `.Add` if it finds it isn't `null`. – Marc Gravell Aug 21 '12 at 09:25
  • I feel that if I'm serializing an object with an IEnumerable property, there's clear semantics around how that is used. If I have some underlying behavior in the backing implementation that must be faithfully restored for behavior, not just data, then why am I serializing the object in the first place? Perhaps I have a narrow view of what kind of structures people serialize data for, because with how I use serialization, I ultimately don't care what backs the property (I'm not downcasting to the underlying object later). Thanks for the work on this, and I'll check out the shim suggestion. – G-Mac Aug 21 '12 at 16:13
  • @G-Mac I'll tell you what: I'll do a survey later over the other comparable serializers - XmlSerializer, DataContractSerializer, JSON.NET and JavaScriptSerializer. If any of them support it, I'll see what I can do... – Marc Gravell Aug 21 '12 at 17:14
  • @G-Mac I checked; looks like DataContractSerializer and JavaScriptSerializer both support this usage, so I've added it to the list for implementation – Marc Gravell Aug 22 '12 at 06:48
  • Fantastic! Thanks Marc. I look forward to revisiting this. – G-Mac Aug 23 '12 at 02:32
  • I am working on this again and was looking to incorporate protobuf into our project. Using the latest version on nuget I am still getting the error "No serializer defined for type: System.Collections.Generic.IEnumerable`1". I see in r584 you added a sample referencing serializing naked enumerables, but it doesn't look like it ever got implemented. Can you confirm? – G-Mac Apr 14 '13 at 05:11
  • @G-Mac yes, I noticed that on Friday. What exactly is the scenario? The problematic case is serializing a Foo : IEnumerable-of-T but without an Add - is that your example? Or is yours a different scenario? – Marc Gravell Apr 14 '13 at 09:59
  • @MarcGravell yes. I expose the properties on my models as IEnumerable, and I want to cache them as copies through serialization. Right now we're using the BinaryFormatter, but as an optimization I'm looking for a more compact and faster alternative. I was able to get some of my models working with the protobuf-net serializer, but it req'd me to change all my proeprties from IEnumerable to IList. When you take a dependency on IEnumerable, you're just abstracting the semantics of accessing that data. Whether there's an array or List or something else behind it is irrelevant. – G-Mac Apr 14 '13 at 17:33
  • @G-Mac if you expect to deserialize it back as a Foo, it matters :) however, you can use SerializeItems / DeserializeItems in that scenario, no? – Marc Gravell Apr 14 '13 at 19:16
  • @MarcGravell I've taken this to a gist to try to explain it better. https://gist.github.com/gmcelhanon/5391894 – G-Mac Apr 15 '13 at 22:41
  • 1
    @G-Mac I can't fault your argument; I can support that scenario - will have it working in the next deploy – Marc Gravell Apr 16 '13 at 08:21
  • @MarcGravell what about, say, `IReadOnlyList`? I have written (mutable) classes that implement this interface and of course one can wrap a `List` in an object that implements `IReadOnlyList`, so it seems like it should be possible to deserialize into an `IReadOnlyList` field too. – Qwertie Aug 25 '13 at 18:36
  • Of course, there are two ways one might want a `IReadOnlyList` or `IEnumerable` field handled: (1) sometimes we don't care what the actual type is. We can use a `repeated` field and deserialize to any available type such as `List` (2) sometimes we want to preserve the original collection type in deserialization, and perhaps even treat the collection itself (as opposed to its elements) `AsReference`. (Note, user may want to select (1) or (2) on a field-by-field or type-by-type basis.) I have the sense protobuf-net doesn't support the second scenario, correct me if I'm wrong? – Qwertie Aug 25 '13 at 18:39
  • @Qwertie in theory, fine: but it needs code to recognise the pattern, implement it, test it, and regression test the existing supported scenarios. all doable, if there's a genuine need – Marc Gravell Aug 25 '13 at 18:39
  • @Qwertie collections are largely an implementation detail - at the moment, no information whatsoever is stored about the container. – Marc Gravell Aug 25 '13 at 18:41
1

I was trying to cut corners, and to reuse my model as a message contract, but this is actually wrong aproach. The way to go is to create DTO's specialized classes and converter to you model, anyway it's good to separate your model from messages.

I must criticize Marc though, for adding ProtoContract and ProtoMemeber attributes that lure users to reuse their model by attributing it.

Alex Burtsev
  • 12,418
  • 8
  • 60
  • 87
  • 1
    Critique can be a good thing, but I'm intrigued what you would have me do instead (and think here too about XmlSerializer's attributes, DataContractSerializer's attributes, etc). As it happens, in v2 *everything* can be done without attributes if you prefer. And I'll happily offer more input if I can see a full-enough-to-run scenario (at the moment, I only have a fragment of one class, which doesn't make it easy to understand the context and offer input) – Marc Gravell Oct 17 '11 at 15:54