I'm using this function to deserialise the data of the xml file:
Uri uri = new Uri("/XML/ligne1_EUROPE.xml", UriKind.Relative);
XDocument document = XDocument.Load("XML/ligne1_EUROPE.xml");
XmlSerializer serializer = new XmlSerializer(typeof(Destinataires));
Destinataires ArretLoad = (Destinataires)serializer.Deserialize(document.CreateReader());
listBox.ItemsSource = ArretLoad.Collection;
He call the Destinataires class:
[XmlRoot("root")]
public class Destinataires
{
[XmlArray("Destinataires")]
[XmlArrayItem("Destinataire")]
[XmlArrayItem("Horraires")]
[XmlArrayItem("Horraire")]
public ObservableCollection<XML_Arret> Collection { get; set; }
}
And call the XML_Arret class:
public class XML_Arret
{
[XmlElement("Designation")]
public string Designation { get; set; }
[XmlElement("Carre")]
public string Carre { get; set; }
[XmlElement("Horraires")]
public Horraires[] Horaires { get; set; }
public class Horraires
{
[XmlElement("Horraire")]
public string Horraire { get; set; }
}
}
My XML files is:
<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<Destinataires>
<Destinataire>
<Designation>Faubourd d'isle</Designation>
<Carre>images/1.png</Carre>
<Horraires>
<Horraire>6h18</Horraire>
<Horraire>6h28</Horraire>
<Horraire>6h38</Horraire>
...
</Horraires>
</Destinataire>
...
</Destinataires
</root>
But when I debug my programm, I've this message:
Une exception de type 'System.InvalidOperationException' s'est produite dans System.Xml.Serialization.ni.dll mais n'a pas été gérée dans le code utilisateur
at the line:
XmlSerializer serializer = new XmlSerializer(typeof(Destinataires));
So, I'm lost, Do you can Help me?
And I'm sorry for my langage, I'm a french person.
Thanks
I've change my code for:
[XmlRoot("root")]
public class Destinataires
{
[XmlArray("Destinataires")]
[XmlArrayItem("Destinataire")]
public ObservableCollection<XML_Arret> Collection { get; set; }
}
But I've a problem:
I use this function:
private void ArretList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var app = App.Current as App;
app.selectArret = (XML_Arret) listBox.SelectedItem;
var arret = app.selectArret.Designation;
MessageBox.Show(arret);
this.NavigationService.Navigate(new Uri("/BUS_InfoArret.xaml?Arret=" + arret, UriKind.Relative));
}
In my app.xaml.cs I've:
public XML_Arret selectArret { get; set; }
And In my BUS_InfoArret:
XML_Arret arret;
public BUS_InfoArret()
{
InitializeComponent();
var app = App.Current as App;
arret = app.selectArret;
Designation.Text = arret.Designation.ToString(); <-- Work
for (int i = 0; i < arret.Horaires.Length; ++i) <-- Display Ville_st_quentin.XML_Arret+Horraires
{
foreach (var item in arret.Horaires)
{
listBox1.Items.Add(item);
}
}
}
Horraire and Destinaire are both annotated to have the XML tag "Horraire" which is confusing the serializer, change the Destinaire class to :
[XmlRoot("root")]
public class Destinataires
{
[XmlArray("Destinataires")]
[XmlArrayItem("Destinataire")]
public ObservableCollection<XML_Arret> Collection { get; set; }
}
Related
I am trying to serialize a C# object into XML so that it could be used as the body of API call. They are very particular about the input they need. I have built the following class to hold the data I need to send over to them. Including attributes and all properties as Elements instead of attributes. They also require that lists include the type="array" I though that creating my own class the implements a List would be the easiest since all lists I give them must have the same attribute. When serialization occurs it serializes the base class of List items but it doesn't include the attribute I want from the derived class.
public class CustomArray<T> : List<T>
{
[XmlAttribute]
public string type { get; set; } = "array";
}
[XmlRoot("message")]
public class MessageBody
{
[XmlArray("Checks"), XmlArrayItem("CheckItem")]
public CustomArray<Check> CheckList { get; set; }
}
public class Check
{
[XmlElement("C_CHECK_NUMBER")]
public string CheckNumber { get; set; }
[XmlElement("C_CHECK_AMOUNT")]
public decimal Amount { get; set; }
[XmlArray("InvoiceList"), XmlArrayItem("Invoice")]
public CustomArray<Invoice> InvoiceList { get; set; }
}
public class Invoice
{
[XmlElement("C_INVOICE_ID")]
public long ID { get; set; }
[XmlElement("C_INVOICE_NUM")]
public string InvoiceNum { get; set; }
}
I then run this code:
// Create a sample object
var message = new MessageBody()
{
CheckList = new CustomArray<Check>
{
new Check
{
CheckNumber = "111",
Amount = 1.00M
},
new Check
{
CheckNumber = "112",
Amount = 2.00M,
InvoiceList = new CustomArray<Invoice>
{
new Invoice
{
ID = 1,
InvoiceNum = "1"
}
}
}
}
};
// Create custom settings
var settings = new XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent = true
};
// Serialize item and print it to console
using (var sw = new StringWriter())
using (var writer = XmlWriter.Create(sw, settings))
{
var serializer = new XmlSerializer(message.GetType());
serializer.Serialize(writer, message, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
Console.WriteLine(sw.ToString());
}
I get this written to the console:
<message>
<Checks>
<CheckItem>
<C_CHECK_NUMBER>111</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>1.00</C_CHECK_AMOUNT>
</CheckItem>
<CheckItem>
<C_CHECK_NUMBER>112</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>2.00</C_CHECK_AMOUNT>
<InvoiceList>
<Invoice>
<C_INVOICE_ID>1</C_INVOICE_ID>
<C_INVOICE_NUM>1</C_INVOICE_NUM>
</Invoice>
</InvoiceList>
</CheckItem>
</Checks>
</message>
But I need to get this:
<message>
<Checks type="array">
<CheckItem>
<C_CHECK_NUMBER>111</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>1.00</C_CHECK_AMOUNT>
</CheckItem>
<CheckItem>
<C_CHECK_NUMBER>112</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>2.00</C_CHECK_AMOUNT>
<InvoiceList type="array">
<Invoice>
<C_INVOICE_ID>1</C_INVOICE_ID>
<C_INVOICE_NUM>1</C_INVOICE_NUM>
</Invoice>
</InvoiceList>
</CheckItem>
</Checks>
</message>
Thank you for your help!
Here is a dotnetfiddle that I made to show it off. It's not exact but it has the same idea. https://dotnetfiddle.net/ALCX5H
Try following :
[XmlRoot("message")]
public class MessageBody
{
[XmlElement("Checks")]
public Checks Checks { get; set; }
}
public class Checks
{
[XmlAttribute]
public string type { get; set; }
[XmlElement("Checks")]
public List<Check> Checks { get; set; }
}
public class Check
{
[XmlElement("C_CHECK_NUMBER")]
public string CheckNumber { get; set; }
[XmlElement("C_CHECK_AMOUNT")]
public decimal Amount { get; set; }
}
I need to serialize a class to xml. If a certain condition is met at run-time, I want to add an XML attribute to an element and assign it a value. Sometimes, the "Error" attribute will appear and sometimes it won't.
My code that serializes my objects:
public class XmlToolsRepo : IXmlTools
{
public string SerializeToXML<T>(object obj)
{
string results = null;
Encoding enc = Encoding.UTF8;
using (MemoryStream ms = new MemoryStream())
{
using (XmlTextWriter xw = new XmlTextWriter(ms, enc))
{
xw.Formatting = Formatting.None;
XmlSerializerNamespaces emptyNS = new XmlSerializerNamespaces(new[] { new XmlQualifiedName("", "") });
XmlSerializer xSerializer = new XmlSerializer(typeof(T));
xSerializer.Serialize(xw, obj, emptyNS);
}
results = enc.GetString(ms.ToArray());
}
return results;
}
}
A class with a property that could have a new attribute at run-time:
[DataContract]
public class H204
{
[DataMember]
[XmlAttribute]
public string Code { get; set; }
[DataMember]
public string DW { get; set; }
}
When a condition is met I need for the XML to look like this:
<?xml version="1.0" encoding="UTF-8"?>
<H204 Code="A">
<DW Error="test" />
</H204>
Try following :
public class H204
{
[XmlAttribute(AttributeName = "Code")]
public string Code { get; set; }
[XmlElement(ElementName = "DW")]
public DW dw{ get; set; }
}
public class DW
{
[XmlAttribute(AttributeName = "Error")]
public string text { get; set; }
}
I have a collection class implementing ICollection<T> with a few custom attributes thrown in for completeness..
In this simplistic sample, its a simple Request/Results pattern with the request itself being passed back as an attribute of the results class.
[Serializable]
public class MyRequest
{
public int SearchID { get; set; }
}
[Serializable]
public class MyResults : ICollection<MyElement>
{
public MyRequest RequestDetails { get; set; }
private ICollection<MyElement> _list = new List<MyElement>();
/* ICollection interface methods removed */
}
[Serializable]
public class MyElement
{
public int ID { get; set; }
}
Here's the sample program to instantiate and then output a serialized string.
class Program
{
static void Main(string[] args)
{
MyResults m = new MyResults();
m.RequestDetails = new MyRequest() { SearchID = 1 };
for (int i = 1; i <= 5; i++)
{
m.Add(new MyElement { ID = i });
}
XmlDocument xmlDoc = new XmlDocument();
XmlSerializer xmlSerializer = new XmlSerializer(m.GetType());
using (MemoryStream xmlStream = new MemoryStream())
{
xmlSerializer.Serialize(xmlStream, m);
xmlStream.Position = 0;
xmlDoc.Load(xmlStream);
}
System.Diagnostics.Debug.WriteLine(xmlDoc.OuterXml);
}
}
The problem is that the output is not including the MyRequest details...
<?xml version="1.0"?>
<ArrayOfMyElement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyElement>
<ID>1</ID>
</MyElement>
<MyElement>
<ID>2</ID>
</MyElement>
<MyElement>
<ID>3</ID>
</MyElement>
<MyElement>
<ID>4</ID>
</MyElement>
<MyElement>
<ID>5</ID>
</MyElement>
</ArrayOfMyElement>
XmlSerializer always ignores extra properties when serializing a collection; the only way to do it, as far as I know, is not to implement ICollection<MyElement> on your MyResults class, and instead expose the collection as a property:
public class MyResults
{
public MyRequest RequestDetails { get; set; }
public ICollection<MyElement> Items { get; set; }
}
(the Serializable attribute isn't needed for XML serialization)
Just change ICollection to Collection because XmlSerialization does not support Generic Interfaces:
public class MyResults
{
public MyResults()
{
this.Items= new Collection<MyElement>();
}
public MyRequest RequestDetails { get; set; }
public Collection<MyElement> Items { get; set; }
}
Right now I have a XML file which looks like this :
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMyMedia xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyMedia>
<fileName>C:\Users\Yanis\Pictures\Photos\VID_20130704_203538.mp4</fileName>
<title>VID_20130704_203538.mp4</title>
<length>0</length>
<_type>0</_type>
</MyMedia>
<MyMedia>
<fileName>C:\Users\Yanis\Pictures\Photos\VID_20130802_142038.mp4</fileName>
<title>VID_20130802_142038.mp4</title>
<length>0</length>
<_type>0</_type>
</MyMedia>
</ArrayOfMyMedia>
This XML is generated by this function :
public void WriteXML(List<MyMedia> myMedias)
{
System.Xml.Serialization.XmlSerializer writer = new System.Xml.Serialization.XmlSerializer(typeof(List<MyMedia>));
System.IO.StreamWriter file = new System.IO.StreamWriter(
#"c:\temp\SerializationOverview.xml");
writer.Serialize(file, myMedias);
file.Close();
}
And when I want to retrieve my list of medias of type : List<MyMedias>
With this function :
public void LoadXML()
{
System.Xml.Serialization.XmlSerializer reader = new System.Xml.Serialization.XmlSerializer(typeof(List<MyMedia>));
System.IO.StreamReader file = new System.IO.StreamReader(#"c:\temp\SerializationOverview.xml");
window1.medias = (List<MyMedia>)reader.Deserialize(file);
for (int i = 0; i < window1.medias.Count; i++)
{
window1.mediaList.Items.Add(window1.medias[i]);
}
}
Here is my "MyMedia" class, it's pretty simple though :
public class MyMedia
{
enum type
{
MUSIC,
VIDEO,
IMAGE
};
public String fileName { get; set; }
public String artist { get; set; }
public String title { get; set; }
public int length { get; set; }
public int _type { get; set; }
public MyMedia()
{
}
public override string ToString()
{
return title;
}
}
My window1.medias variable is empty.
Any clue ?
Edit : I found the solution, I had a field in my "MyMedia" class that was not in my .xml file. I removed it and it worked.
When I try to deserialize below XML I get error: Settings xmlns='' was not expected.
I have mapped the root node in my class below, why do I get that error?
my xml:
<Settings>
<Access>
<Phone hasTextField="true">
<Item description="CMS" />
</Phone>
</Access>
</Settings>
class
Settings.cs:
[XmlRoot("Settings")]
public class AccessNodes
{
[XmlElement("Access")]
public Access AccessList { get; set; }
}
[XmlType("Access")]
public class Access
{
[XmlElement("Phone")]
public AccessItem Phone { get; set; }
}
public class AccessItem
{
public AccessItem()
{
Items = new List<Item>();
}
[XmlAttribute("hasTextField")]
public bool HasTextField { get; set; }
[XmlElement("Item")]
public List<Item> Items { get; set; }
}
[XmlType("Item")]
public class Item
{
[XmlAttribute("description")]
public string Description { get; set; }
}
SettingsReader.cs
public static class SettingsReader<T>
{
public static T Deserialize(string basePath)
{
string filename = basePath + ".xml";
T t = default(T);
XmlSerializer xs = new XmlSerializer(typeof(T));
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
t = (T)xs.Deserialize(fs);
}
return t;
}
Call to SettingsReader.cs
Access access = SettingsReader<Access>.Deserialize(Server.MapPath("~/App_Data/access"));
You're trying to deserialize the wrong element - you're trying to deserialize the Access type, whereas your top node is Settings, which is the name applied to the AccessNodes type.
Try this instead:
AccessNodes settings = SettingsReader<AccessNodes>.Deserialize(...);