DataContractSerializer can't find reference? - c#

at first: sorry if this is a long and code heavy question. i am trying to serialize and deserialize a class called level that looks like this:
[DataContract]
public class Level
{
[DataMember]
public List<Face> faces;
public Level()
{
Face a = new Face();
Face b = new Face();
Face c = new Face();
Face d = new Face();
a.edges.Add(new Edge(b, c));
b.edges.Add(new Edge(a, c));
c.edges.Add(new Edge(a, d));
d.edges.Add(new Edge(a, b));
this.faces = new List<Face>() { a, b, c, d };
}
}
[DataContract(IsReference = true)]
public class Face
{
[DataMember]
public List<Edge> edges;
public Face()
{
this.edges = new List<Edge>();
}
}
[DataContract(IsReference = true)]
public class Edge
{
[DataMember]
public Face a;
[DataMember]
public Face b;
public Edge(Face a, Face b)
{
this.a = a;
this.b = b;
}
}
since there are circular references i need to enable the reference function of the datacontract serializer.However when i run this i get the error
SerializationException: Deserialized object with reference Id 'i1' was not found
the serialize and deserialize functions look like this:
// object to be serialized
public Level level;
public void Serialize()
{
DataContractSerializer serializer = new DataContractSerializer(typeof(Level));
FileStream stream = new FileStream(Path.Combine(Application.dataPath, "test.xml"), FileMode.Create);
Debug.Log("serial");
serializer.WriteObject(stream, level);
stream.Close();
}
// function to serialize an object to a json text file
public void Deserialize()
{
FileStream stream = new FileStream(Path.Combine(Application.dataPath, "test.xml"), FileMode.Open);
DataContractSerializer serializer = new DataContractSerializer(typeof(Level));
Level loaded = (Level)serializer.ReadObject(stream);
stream.Close();
}
void Start()
{
level = new Level();
Serialize();
Deserialize();
}
here is also a serialized xml example:
<Level xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/">
<faces>
<Face z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<edges>
<Edge z:Id="i2">
<a z:Id="i3">
<edges>
<Edge z:Id="i4">
<a z:Ref="i1" />
<b z:Id="i5">
<edges>
<Edge z:Id="i6">
<a z:Ref="i1" />
<b z:Id="i7">
<edges>
<Edge z:Id="i8">
<a z:Ref="i1" />
<b z:Ref="i3" />
</Edge>
</edges>
</b>
</Edge>
</edges>
</b>
</Edge>
</edges>
</a>
<b z:Ref="i5" />
</Edge>
</edges>
</Face>
<Face z:Ref="i3" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" />
<Face z:Ref="i5" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" />
<Face z:Ref="i7" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" />
</faces>
</Level>
i searched for an answer for a few days now and still didn't get any close.
Can you give me a hint on how to solve this please?

I think your problem may be that you are trying to deserialize something that you previously serialized before you added the IsReference=true parameter to the attribute.
If you delete your test file and create a new one with your updated code, the deserialization process should work without issue.

Related

Keep null values in List when serializing to XML

I have a list that can contain objects of different types and also nullvalues. When I serialize the class to XML, I want to keep these null values but they get automatically removed. Setting IsNullable = true in the XmlArrayItem attribute did not change anything.
I have the following simplified structure of classes:
public class MyClass
{
[XmlArray("Items")]
[XmlArrayItem("TypeA", typeof(A), IsNullable = true)]
[XmlArrayItem("TypeB", typeof(B), IsNullable = true)]
public ObservableCollection<Base> MyCollection { get; set; }
}
public class Base
{
}
public class A : Base
{
}
public class B : Base
{
}
As said before, MyCollection can contain objects of 2 different types but also null values (in my example at index 0 and 2).
This is my serialization code:
var myClass = new MyClass();
myClass.MyCollection = new ObservableCollection<Base>
{
null,
new A(),
null,
new B()
};
var stream = new FileStream("C:\\temp\\test.xml", FileMode.Create);
var serializer = new XmlSerializer(typeof (MyClass));
serializer.Serialize(stream, myClass);
I get the following XML output:
<?xml version="1.0"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<TypeA />
<TypeB />
</Items>
</MyClass>
How can I keep the null values in the list? I searched for a while but I only found solutions how to remove null-properties.
Hmm, indeed, I haven't succeeded in making working the XmlArrayItem with the IsNullable property but if you don't mind a slightly different XML Output you can try that:
public class MyClass
{
[XmlArray("Items", IsNullable = true)]
public ObservableCollection<Base> MyCollection { get; set; }
}
[XmlInclude(typeof(A))]
[XmlInclude(typeof(B))]
public class Base
{
}
[XmlType("TypeA")]
public class A : Base
{
}
[XmlType("TypeB")]
public class B : Base
{
}
public static void Test()
{
var myClass = new MyClass() { MyCollection = new ObservableCollection<Base> { new A(), null, new B(), null } };
var wtr = new StreamWriter("C:\\avp\\test.xml");
var s = new XmlSerializer(typeof(MyClass));
s.Serialize(wtr, myClass);
wtr.Close();
}
Then you'll get that output:
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<Base xsi:type="TypeA" />
<Base xsi:nil="true" />
<Base xsi:type="TypeB" />
<Base xsi:nil="true" />
</Items>
</MyClass>

C# XmlSerializer xmlns in child element matching parent

I'm using XmlSerializer to create a xml doc in use for ebay's large merchant services.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<BulkDataExchangeRequests xmlns="urn:ebay:apis:eBLBaseComponents">
<Header>
<SiteID>0</SiteID>
<Version>775</Version>
</Header>
<AddFixedPriceItemRequest xmlns="urn:ebay:apis:eBLBaseComponents">
<Version>775</Version>
<Item>
<AutoPay>false</AutoPay>
<BuyerProtection>ItemIneligible</BuyerProtection>
<BuyItNowPrice currencyID="USD">0.0</BuyItNowPrice>
<Country>US</Country>
<Currency>USD</Currency>
<Description>test</Description>
<GiftIcon>0</GiftIcon>
</Item>
</AddFixedPriceItemRequest>
</BulkDataExchangeRequests>
The problem I'm having is getting the AddFixedPriceItemRequest generated by the serializer to actually contain that xmlns like the BulkDataExchangeRequests element has. It seems to be a requirement in order for this to work. I generate the bulk tag using:
writer.WriteStartElement("BulkDataExchangeRequests", "urn:ebay:apis:eBLBaseComponents");
I create a serializer.
serializer = new XmlSerializer(typeof(AddFixedPriceItemRequestType));//, "urn:ebay:apis:eBLBaseComponents");
and serialize with the namespace
request = new AddFixedPriceItemRequestType()
{
//populate data.
};
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add("", "urn:ebay:apis:eBLBaseComponents");
serializer.Serialize(writer, request, namespaces);
This is the type with the xml attributes:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.5420")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:ebay:apis:eBLBaseComponents",TypeName="AddFixedPriceItemRequest")]
public partial class AddFixedPriceItemRequestType : AbstractRequestType {
//filled in class
}
my output ends up like this:
<AddFixedPriceItemRequest xmlns="">
<ErrorLanguage xmlns="urn:ebay:apis:eBLBaseComponents">en_US</ErrorLanguage>
<Version xmlns="urn:ebay:apis:eBLBaseComponents">837</Version>
<Item p4:type="Item" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:ebay:apis:eBLBaseComponents">
Could someone help out with how to get xmlns of addfixedpriceitemrequest set to match the bulk xmlns via the serializer. Or recommend another way to do it. I was trying to avoid writing each property out with createelement/writeelement.
That works for me
ItemType itemType = new ItemType()
{
//populate data.
};
ApiCall apiCall = new AddItemCall() { Item = itemType };
AddItemRequestType addFixedPriceItemRequestType = ((AddItemCall)apiCall).ApiRequest;
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:ebay:apis:eBLBaseComponents")});
XmlSerializer xmlSerializer = new XmlSerializer(
addFixedPriceItemRequestType.GetType(), "urn:ebay:apis:eBLBaseComponents");
MemoryStream memoryStream = new MemoryStream();
xmlSerializer.Serialize(memoryStream, addFixedPriceItemRequestType , namespaces);
memoryStream.Seek((long)0, SeekOrigin.Begin);
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(memoryStream);
memoryStream.Close();
string addItemRequestTypeXML = xmlDocument.OuterXml;
The output is going to be:
<AddItemRequestType xmlns="urn:ebay:apis:eBLBaseComponents">
<Item>
<ApplicationData></ApplicationData>
<AutoPay>false</AutoPay>
<CategoryMappingAllowed>true</CategoryMappingAllowed>
<Country>US</Country>
<Currency>EUR</Currency>
<Description>
....
<AddItemRequestType>
The problem most likely lies in the XmlTypeAttribute - it seems you use your class as the top level container - you should use XmlRootAttribute insted. This way you will get urn:ebay:apis:eBLBaseComponents placed in correct location.
Sample:
void Main()
{
var t = new A {FieldOfB = new B {C = 5}};
var serializer = new XmlSerializer(typeof(A));
using (var writer = new StringWriter())
{
serializer.Serialize(writer, t);
writer.ToString().Dump();
}
}
When used with this class definitions:
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:ebay:apis:eBLBaseComponents")]
public class A
{
public B FieldOfB {get;set;}
}
public class B
{
public int C {get;set;}
}
yields this result:
<?xml version="1.0" encoding="utf-16"?>
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FieldOfB xmlns="urn:ebay:apis:eBLBaseComponents">
<C>5</C>
</FieldOfB>
</A>
And if you fix the attribute:
[System.Xml.Serialization.XmlRootAttribute(Namespace="urn:ebay:apis:eBLBaseComponents")]
public class A
{
public B FieldOfB {get;set;}
}
public class B
{
public int C {get;set;}
}
You get this:
<?xml version="1.0" encoding="utf-16"?>
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:ebay:apis:eBLBaseComponents">
<FieldOfB>
<C>5</C>
</FieldOfB>
</A>
eBay requires namespace setting in Trade API only. One solution is to remove namespace setting in BulkDataExchange objects, and add namespace setting in Trade API object only. This is not a standard XML format, however eBay only accept this.
In your case, this might do the trick to eBay:
writer.WriteStartElement("BulkDataExchangeRequests", "");

How to include null properties during xml serialization

Currently, the code below omits null properties during serialization. I want null valued properties in the output xml as empty elements. I searched the web but didn't find anything useful. Any help would be appreciated.
var serializer = new XmlSerializer(application.GetType());
var ms = new MemoryStream();
var writer = new StreamWriter(ms);
serializer.Serialize(writer, application);
return ms;
Sorry, I forgot to mention that I want to avoid attribute decoration.
Can you control the items that have to be serialized?
Using
[XmlElement(IsNullable = true)]
public string Prop { get; set; }
you can represent it as <Prop xsi:nil="true" />
You can use also use the following code. The pattern is ShouldSerialize{PropertyName}
public class PersonWithNullProperties
{
public string Name { get; set; }
public int? Age { get; set; }
public bool ShouldSerializeAge()
{
return true;
}
}
PersonWithNullProperties nullPerson = new PersonWithNullProperties() { Name = "ABCD" };
XmlSerializer xs = new XmlSerializer(typeof(nullPerson));
StringWriter sw = new StringWriter();
xs.Serialize(sw, nullPerson);
XML
<?xml version="1.0" encoding="utf-16"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://
www.w3.org/2001/XMLSchema">
<Name>ABCD</Name>
<Age xsi:nil="true" />
</Person>
Set the XmlElementAttribute.IsNullable property:
https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlelementattribute.isnullable(v=vs.110).aspx

Problems with serialization of NameKeyValue and List<>

I'll try to show my problems.
I have a list of pairs key-values that I want to serialize to a XML file. I've read others questions (question - question) in this site and finally I have done this:
[Serializable]
[XmlType(TypeName = "Pair")]
public class NameValuePair
{
public String Key { get; set; }
public Object Value { get; set; }
public NameValuePair()
{
Key = null;
Value = null;
}
public NameValuePair(String k, Object v)
{
Key = k;
Value = v;
}
}
[Serializable]
[XmlType(TypeName = "FileTransfer_Configuration_File")]
public class ConfigTable : List<NameValuePair>
{
//more code
}
//Serialization example
XmlSerializer serializer = new XmlSerializer(typeof(ConfigTable));
using (StreamWriter writer = new StreamWriter(file))
{
serializer.Serialize(writer, configtable);
}
And the result is perfectly OK:
<?xml version="1.0" encoding="utf-8"?>
<FileTransfer_Configuration_File xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Pair>
<Key>ini_Win</Key>
<Value xsi:type="xsd:boolean">true</Value>
</Pair>
<Pair>
<Key>son_Peticion</Key>
<Value xsi:type="xsd:string">defecto</Value>
</Pair>
</FileTransfer_Configuration_File>
The problem comes when the value attribute of the NameValuePair is a List<> object. List<> objects are serializable, but not inside a NameValuePair:
List<Usuario> aux = new List<Usuario>();
aux.Add(new Usuario("pepito"));
aux.Add(new Usuario("juanito"));
aux.Add(new Usuario("miguelito", true, "c:/miguelito/"));
NameValuePair nvp = new NameValuePair("clave", aux);
XmlSerializer serializer = new XmlSerializer(typeof(NameValuePair));
using (StreamWriter writer = new StreamWriter(file))
{
serializer.Serialize(writer, nvp);
}
The result: 'System.InvalidOperationException' in System.Xml.dll and this output:
<?xml version="1.0" encoding="utf-8"?>
<Pair xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Key>clave</Key>
//abrupt tnd of file
How can I do what I want? Thank you

C# Array XML Serialization

I found a problem with the XML Serialization of C#. The output of the serializer is inconsistent between normal Win32 and WinCE (but surprisingly WinCE has the IMO correcter output). Win32 simply ignores the Class2 XmlRoot("c2") Attribute.
Does anyone know a way how to get the WinCE like output on Win32 (because i don't want the XML tags to have the class name of the serialization class).
Test Code:
using System;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleTest
{
[Serializable]
[XmlRoot("c1")]
public class Class1
{
[XmlArray("items")]
public Class2[] Items;
}
[Serializable]
[XmlRoot("c2")]
public class Class2
{
[XmlAttribute("name")]
public string Name;
}
class SerTest
{
public void Execute()
{
XmlSerializer ser = new XmlSerializer(typeof (Class1));
Class1 test = new Class1 {Items = new [] {new Class2 {Name = "Some Name"}, new Class2 {Name = "Another Name"}}};
using (TextWriter writer = new StreamWriter("test.xml"))
{
ser.Serialize(writer, test);
}
}
}
}
Expected XML (WinCE generates this):
<?xml version="1.0" encoding="utf-8"?>
<c1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<items>
<c2 name="Some Name" />
<c2 name="Another Name" />
</items>
</c1>
Win32 XML (seems to be the wrong version):
<?xml version="1.0" encoding="utf-8"?>
<c1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<items>
<Class2 name="Some Name" />
<Class2 name="Another Name" />
</items>
</c1>
Try [XmlArrayItem("c2")]
[XmlRoot("c1")]
public class Class1
{
[XmlArray("items")]
[XmlArrayItem("c2")]
public Class2[] Items;
}
or [XmlType("c2")]
[XmlType("c2")]
public class Class2
{
[XmlAttribute("name")]
public string Name;
}

Resources