I have a list of objects that implement a common interface. If I try to simply serialize it I get a nice exception that tells me that the serializer cannot serialize interfaces:
private readonly ObservableCollection<ICanHasInterface> children = new ObservableCollection<ICanHasInterface>();
public ObservableCollection<ICanHasInterface> Children
{
get { return children; }
}
=> "Cannot serialize member ... of type ... because it is an interface"
Apparently asking the serializer to get the type of the objects and mark the XmlElement with the attribute xsi:type (which is done if an object inherits from another class) is too much.
So because I do not want to implement IXmlSerializable, I thought up a workaround which looked promising initially:
private readonly ObservableCollection<ICanHasInterface> children = new ObservableCollection<ICanHasInterface>();
[XmlIgnore()]
public ObservableCollection<ICanHasInterface> Children
{
get { return children; }
}
[XmlElement("Child")]
public List<object> ChildrenSerialized
{
get
{
return new List<object>(Children);
}
set
{
Children.Clear();
foreach (var child in value)
{
if (child is ICanHasInterface) AddChild(child as ICanHasInterface);
}
}
}
With this at least the serialisation works just fine (Note: Either specify XmlInclude attributes for the types that can be in the original list or hand over an array of types in the constructor of the serializer), however if the object is deserialized the Children collection ends up empty because the set block is never reached during deserialization, I am quite clueless as to why this is; any ideas?
On deserialization the serializer uses your property getter to get the collection instance and then calls Add() on it for each item. It does not call your property setter. Something like this:
YourClass c = new YourClass();
c.ChildrenSerialized.Add(ReadValue());
...
In order to keep the collections synchronized you'd need to customize the Add() behavior of the collection you return from the property getter.
A better option is to change the ChildrenSerialized property to use an object[]. For arrays, the serializer reads the value into an array and then calls your property setter with the value.
Related
I have a Serializer/Deserializer that use a references PreserveReferencesHandling = PreserveReferencesHandling.All.
The issue, is that I have circular references.
Here is a very simple example.
class Node
{
public Node(object value)
{
Value = value;
}
public object Value { get; set; }
public Node Left { get; set; }
public Node Right { get; set; }
}
My test scenario is:
var obj = new Node("o")
{
Left = new Node("oL"),
Right = new Node("oR")
};
obj.Right.Right = obj; // Circular reference!
When I deserialize, i have the following IReferenceResolver
private class InternalReferenceResolver : IReferenceResolver
{
private readonly Deserializer _par;
public InternalReferenceResolver(Deserializer par)
{
_par = par;
}
public object ResolveReference(object context, string reference)
{
object refObject;
if (!_par._processed.TryGetValue(reference, out refObject))
{
refObject = _par.DeserializeObject(reference);
}
return refObject;
}
public string GetReference(object context, object value)
{
throw new NotSupportedException("Only for Serialization");
}
public bool IsReferenced(object context, object value)
{
return false;
}
public void AddReference(object context, string reference, object value)
{
_par._processed.Add(reference, value);
}
}
As you can see, when JSON.NET inform me of a new ref->object (via AddReference()) I add it to a dictionary.
When JSON.NET request an object for a specific reference (via ResolveReference()) I recurse, and deserialize that reference.
The issue is, JSON.NET calls ResolveReference() for each of the object references, before it calls it's AddReference().
I would expect the flow of Deserialization to be:
Identify Object Type
Construct the object
AddReference(id, newObj)
Resolve References + Populate Properties
What I see happens is:
Identify Object Type
Resolve References
Construct the object
AddReference(id, newObj)
Populate Properties
My Questions:
Why is it made the latter, am i missing something with my suggested flow?
how can I overcome this issue, having a "Bare" object just for referencing and only then actually resolve the references ?
The Two-Phase deserialization you are seeing is arises because your Node class only has a parameterized constructor. As explained in Issue with serializing/deserializing object graph with self-referencing objects in combination with JSON constructor. #715:
JamesNK commented on Nov 28, 2015
Non-default constructors and preserving references don't work well together because the child values of a type have to be deserialized before the parent is created, so the reference resolves to null.
Thus, since Value is a mutable property anyway, you should add a parameterless constructor to Node:
class Node
{
public Node() : this(null) { }
public Node(object value)
{
Value = value;
}
// Remainder unchanged.
}
It can be non-public if you mark it with [JsonConstructor] or deserialize using the setting ConstructorHandling.AllowNonPublicDefaultConstructor. And if Value were immutable, you would need to make it privately settable and mark it with [JsonProperty]
[JsonProperty]
public object Value { get; private set; }
(Data contract attributes can be used in place of Json.NET attributes if you prefer.)
Notes:
Since your question lacks a complete and verifiable example, your code might encounter other problems that aren't fixed by adding a parameterless constructor.
For a related question, see Usage of non-default constructor breaks order of deserialization in Json.net.
For a related issue, see PreserveReferencesHandling.Objects deserialize does not work with non-default constructor #678.
Another thing, can I tell Json.NET to only handle the constructor parameters and leave the rest... ?
Not according to Issue 715. Since a JSON object is an unordered set of name/value pairs, Json.NET needs to parse the entire object to ensure it has loaded all the constructor parameters. As it is a single-pass serializer the non-constructor parameters will get loaded into... something... before the object can be constructed. Json.NET has chosen to deserialize them in one step to the final target member type rather than to an intermediate JToken and later the final member type. This can be see in JsonSerializerInternalReader.ResolvePropertyAndCreatorValues().
Well, I Found a solution for my problem:
The first Deserialization i perform, i use a custom IContractResolver that exclude all properties that are not relevant to the constructor...
at the second pass, I use Populate and use the default IContractResolver
private class InternalOnlyCtorContractResolver : IContractResolver
{
private readonly IContractResolver _base;
public InternalOnlyCtorContractResolver(IContractResolver _base)
{
this._base = _base;
}
public JsonContract ResolveContract(Type type)
{
var contract = _base.ResolveContract(type);
var objectContract = contract as JsonObjectContract;
if (objectContract != null)
{
var creatorParameters = new HashSet<string>(objectContract.CreatorParameters.Select(p => p.PropertyName));
var irrelevantProperties = objectContract.Properties
.Where(p => !creatorParameters.Contains(p.PropertyName))
.ToArray();
foreach (var irrelevantProperty in irrelevantProperties)
{
objectContract.Properties.Remove(irrelevantProperty);
}
//TODO Can be optimized better
}
return contract;
}
}
If for some reason, the Circular Reference is needed by the Constructor,
It still cause a loop, but it is kinda impossible to create without having a second constructor anyway.
When serializing an object, I have a List<T> property where T is an abstract type which I'd like to serialize naturally. However, when deserializing the object, I want/need to manually deserialize this abstract list. I'm doing the latter part with a custom JsonConverter which looks at a property on each item
in the list and then deserializes it to the correct type and puts it in the list.
But I also need to deserialize the rest of the object, without doing it property-by-property. Pseudo-code follows.
MyObject
class MyObject {
public Guid Id;
public string Name;
public List<MyAbstractType> Things;
}
MyObjectConverter
class MyObjectConverter : JsonConverter {
public override object ReadJson(reader, ..., serializer) {
// Line below will fail because the serializer will attempt to deserlalize the list property.
var instance = serializer.Deserialize(reader);
var rawJson = JObject.Load(reader); // Ignore reading from an already-read reader.
// Now loop-over and deserialize each thing in the list/array.
foreach (var item in rawJson.Value<JArray>(nameof(MyObject.Things))) {
var type = item.Value<string>(nameof(MyAbstractType.Type));
MyAbstractType thing;
// Create the proper type.
if (type == ThingTypes.A) {
thing = item.ToObject(typeof(ConcreteTypeA));
}
else if (type == ThingTypes.B) {
thing = item.ToObject(typeof(ConcreteTypeB));
}
// Add the thing.
instance.Things.Add(thing);
}
return instance;
}
}
To recap, I want to manually handle the deserialization of Things, but allow them to naturally serialize.
So I have an Object called FormType.
It contains some strings, booleans etc.
But FormType also contains this:
private IList<FormTypeVersion> _versions = new List<FormTypeVersion>();
public virtual IList<FormTypeVersion> Versions
{
get { return _versions; }
set { _versions = value; }
}
Is this why I am getting this error:
{"Cannot serialize member 'Domain.FormType.Versions' of type 'System.Collections.Generic.IList`1
Also - FormTypeVersion also contains some ILists.
How can I get round this error, it happens at this line:
var xm = new XmlSerializer(typeof(T));
The XmlSerializer cannot deserialize interfaces (unless you want to implement IXmlSerializable yourself on the FormType object). That is why you are seeing that exception.
If you change your IList to List it should work like in the following example:
[Serializable]
public class FormType
{
private List<FormTypeVersion> _versions = new List<FormTypeVersion>();
public virtual List<FormTypeVersion> Versions
{
get { return _versions; }
set { _versions = value; }
}
}
If you don't have the luxury to change your type from IList to List, then the cleanest approach is to implement IXmlSerializable. There are other solutions using abstract types, reflection and similar, but i wouldn't call that clean.
I have a collection property that is of a custom type which inherits from BindingList. Currently, this property gets serialized via XmlSerializer even though it has no Setter. I now am trying to implement IXmlSerializable on this custom collection class and see that the WriteXml() and ReadXml() interface methods only get called if my collection property has a Setter. Why does serialization ignore this property now unless there is a Setter when before it serialized correctly without one.
To Reproduce:
First, have a class called "Item":
public class Item
{
public Item() {}
// Generates some random data for the collection
private MyCollectionType GenerateContent()
{
Random ranGen = new Random();
MyCollectionType collection = new MyCollectionType();
for (int i = 0; i < 5; i ++)
{
collection.Add("Item" + ranGen.Next(0,101));
}
return collection;
}
public MyCollectionType Items
{
get
{
if (m_Items == null)
{
m_Items = GenerateContent();
}
return m_Items;
}
}
private MyCollectionType m_Items = null;
}
Next have create the collection Class "MyCollectionType" (Note that IXmlSerializable is purposely missing in the snippet to start off with):
public class MyCollectionType : BindingList<string>
{
public MyCollectionType()
{
this.ListChanged += MyCollectionType_ListChanged;
}
void MyCollectionType_ListChanged(object sender, ListChangedEventArgs e){ }
public MyCollectionType(ICollection<string> p)
{
this.ListChanged += MyCollectionType_ListChanged;
}
#region Implementation of IXmlSerializable
public void WriteXml(XmlWriter writer)
{
throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public XmlSchema GetSchema() { return null; }
#endregion
}
Lastly, add some code in Main() to Serialize and Deserialize an "Item":
Item myItem = new Item();
Item newItem = null;
// Define an XmlSerializer
XmlSerializer ser = new XmlSerializer(typeof(Item));
// Serialize the Object
using (TextWriter writer = File.CreateText(#"c:\temporary\exportedfromtest.xml"))
{
ser.Serialize(writer,myItem);
}
// Deserialize it
using (Stream reader = new FileStream(#"c:\temporary\exportedfromtest.xml", FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (XmlDictionaryReader xmlDictionaryReader = XmlDictionaryReader.CreateTextReader(reader, XmlDictionaryReaderQuotas.Max))
{
newItem = (Item)ser.Deserialize(xmlDictionaryReader);
}
}
So, if you run that as-is you should see that it serializes and deserializes without a Setter. Currently, the collection doesn't list "IXmlSerializable" in the snippet above, but the methods are there. So if you now go back and add "IXmlSerializable" to the MyCollectionType class and run again you will notice that the collection property isn't serialized and the WriteXml() and ReadXml() methods don't get called. Also note that if you add an empty Setter those methods will suddenly get called.
This is the documented behavior. It is explained, albeit unclearly, in Introducing XML Serialization:
XML serialization does not convert methods, indexers, private fields, or read-only properties (except read-only collections). To serialize all an object's fields and properties, both public and private, use the DataContractSerializer instead of XML serialization.
As you can see, get-only properties are in general not serialized -- except for read-only collections. But what does Microsoft mean by this? A collection is not a property after all.
What they mean is as follows:
XML serialization does not convert get-only properties or read-only fields (except get-only collection-valued properties with pre-initialized collections).
(Incidentally, this means that, if you add items to your collection in the containing type's constructor, then serialize and deserialize it, the default items will get duplicated. For an explanation of why, see XML Deserialization of collection property with code defaults. If I deserialize your Item class I see this behavior.)
How does this apply in your case? Well, when you make your collection implement IXmlSerializable, the serializer no longer interprets it as a collection; it interprets it as a black box. Thus its special rules for collections no longer apply. The property referring to your collection must now be read/write; XmlSerializer will construct an instance itself, deserialize it, and set it into its parent just like any other non-collection property value.
I know that you can't serialize/deserialize using an interface but I'm confused by behaviour I'm seeing.
When I deserialize and cast back to the interface, some properties are null. But if I cast back to the concrete type the same property has a value?
So, given this XML (shortened for brevity):
<Page>
<ComponentPresentations>
<ComponentPresentation>
<Component>
<Categories>
<Category>
<Id>tcm:35-540-512</Id>
Deserializing with
var serializer = new XmlSerializer(typeof(Page));
page = (IPage)serializer.Deserialize(reader);
page.ComponentPresentations[0].Component.Categories <-- is null
But if I cast back to the type,
var serializer = new XmlSerializer(typeof(Page));
page = (Page)serializer.Deserialize(reader);
page.ComponentPresentations[0].Component.Categories <-- is not null!
The Page type exposes the interface Categories property and a non-interface property - I assume to get around the serializing interface problem.
public List<Category> Categories { get; set; }
[XmlIgnore]
IList<ICategory> IComponent.Categories
{
get { return Categories as IList<ICategory>; }
}
Is this because the interface property doesn't expose a setter?
No. The problem is Contravariance not being supported by List<T> and IList<T>. Here is a good reference.
Have a look at this simple code:
public interface IMyInterface
{
}
public class MyImplementation : IMyInterface
{
}
List<MyImplementation> myImplementations = new List<MyImplementation>();
Console.WriteLine(myImplementations as IList<IMyInterface> == null); // OUTPUT: true!!
So as you can see, Categories as IList<ICategory> will always be null. While Categories as IList<Category> will be OK.
Nothing to do with serialisation.