Deserialize XML List<MyType> - c#

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.

Related

Incorrect XML structure c# [duplicate]

I am having an issue with serializing and object, I can get it to create all the correct outputs except for where i have an Element that needs a value and an attribute. Here is the required output:
<Root>
<Method>Retrieve</Method>
<Options>
<Filter>
<Times>
<TimeFrom>2009-06-17</TimeFrom>
</Times>
<Document type="word">document name</Document>
</Filter>
</Options>
</AdCourierAPI>
I can build all of it but can not find a way to set the Document type attribute, here is a segment of the object class
[XmlRoot("Root"), Serializable]
public class Root
{
[XmlElement("Method")]
public string method="RetrieveApplications";
[XmlElement("Options")]
public _Options Options;
}
public class _Options
{
[XmlElement("Filter")]
public _Filter Filter;
}
public class _Filter
{
[XmlElement("Times")]
public _Times Times;
[XmlElement("Documents")]
public string Documents;
}
which gives me:
<Document>document name</Document>
rather than:
<Document type="word">document name</Document>
but I can not find a way to correct this, please advise.
Thanks
Where do you have the type stored?
Normally you could have something like:
class Document {
[XmlAttribute("type")]
public string Type { get; set; }
[XmlText]
public string Name { get; set; }
}
public class _Filter
{
[XmlElement("Times")]
public _Times Times;
[XmlElement("Document")]
public Document Document;
}
The string class doesn't have a type property, so you can't use it to create the desired output. You should create a Document class instead :
public class Document
{
[XmlText]
public string Name;
[XmlAttribute("type")]
public string Type;
}
And you should change the Document property to type Document
It sounds like you need an extra class:
public class Document
{
[XmlAttribute("type")]
public string Type { get; set; }
[XmlText]
public string Name { get; set; }
}
Where an instance (in the example) would have Type = "word" and Name = "document name"; documents would be a List<Document>.
By the way - public fields are rarely a good idea...
You can use XmlWriter instead XmlSerialization to get this effect.
It is more complex but if you have a lot of strings in model it will be cleaner solution.
Create your own CustomeAttribute, for example:
[System.AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class MyCustomAttribute : System.Attribute
{
public MyCustomAttribute (string type)
{
MyType = type;
}
public string MyType { get; set; }
}
Then in model add it, like that:
public class MyModel
{
[MyCustom("word")]
public string Document { get; set; }
[MyCustom("time")]
public string Time { get; set; }
}
The last part is to create xml with this arguments.
You can do it likes that:
var doc = new XmlDocument();
MyModel myModel = new MyModel();//or get it from somewhere else
using (Stream s = new MemoryStream())
{
var settings = new XmlWriterSettings();
settings.Async = true;
settings.Indent = true;
var writer = XmlTextWriter.Create(s, settings);
await writer.WriteStartDocumentAsync();
await writer.WriteStartElementAsync(null,"Root", null);
myModel.GetType().GetProperties().ToList().ForEach(async p =>
{
dynamic value = p.GetValue(myModel);
writer.WriteStartElement(p.Name);
var myCustomAttribute = p.GetCustomAttributes(typeof(MyCustomAttribute), false).FirstOrDefault() as MyCustomAttribute;
if(myCustomAttribute != null)
{
await writer.WriteAttributeStringAsync(null, "MyType", null, myCustomAttribute.MyType );
}
writer.WriteValue(value);
await writer.WriteEndElementAsync();
});
await writer.WriteEndElementAsync();
await writer.FlushAsync();
s.Position = 0;
doc.Load(s);
writer.Close();
}
string myXml = doc.OuterXml
In myXml should be something like that:
(values are examples)
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Document MyType="word">something</Document>
<Time MyType="time">11:31:29</Time>
</Root>
You can do it in other way, of course.
Here you have some docs which helped me:
https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmlwriter?view=netframework-4.8#writing_elements

XML Serializing information on Base class and inherited class

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; }
}

xml serialiser with lists and other objects

I've been looking here for a sollution, but I did not find it in previous answers.
I need to create the following xml with the serialiser
<?xml version="1.0" encoding="Windows-1252"?>
<documents xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<document>
<Keys>
<drawer>GraphicData</drawer>
<somedata>otherData</somedata>
</Keys>
<otherGenericProperties>
<Data>GenericData 2</Data>
</otherGenericProperties>
<Repeat>
<FileInfo mimeType="application/pdf" HREF="PdfFile.pdf" />
</Repeat>
<Repeat>
<FileInfo mimeType="application/pdf" HREF="PdfFile2.pdf" />
</Repeat>
</document>
</documents>
The data consist of a few classes
namespace test
{
public class Documents
{
[XmlElement("Keys")] // this is used so the destination name can change in the future
public Keys keys { get; set; }
}
public class document
{
[XmlElement("Keys")] // this is used so the destination name can change in the future
public Keys keys { get; set; }
[XmlElement("Repeat")] // This does not work
List<Repeat> listRepeat { get; set; }
}
public class Keys
{
[XmlElement("drawer")]
public string drawer { get; set; }
}
public class Repeat
{
[XmlElement("fileInfo")]
public FileInfo fileInfo { get; set; }
}
public class FileInfo
{
public string FileInfo { get; set; }
[XmlAttribute("mimeType")]
public string mimeType { get; set; }
[XmlAttribute("mimeType")]
public string HREF { get; set; }
}
}
the serialiser :
XmlSerializer serializer = new XmlSerializer(typeof(Documents));
using (StreamWriter writer = new StreamWriter(saveBestand, false, xmlEncoding))
{
serializer.Serialize(writer, icm, namespaces);
}
I really need the xm like the example, and also the xml names should be changable by a contract like xmlElement is used for. Somehow the repeat element cant be placed on the level you see in the example. Does anyone have a sollution ?
you have two options.
Option 1
use xsd.exe to generate classes using the xml you want.
xsd yourxmlfile.xml
xsd yourxmlfile.xsd /classes
This will generate classes which you can use for serialization or deserialization.
Option 2:
Paste special option to generate the c# classes.
This does not involve using the command line.
refer blog at: https://dennymichael.net/2014/05/30/convert-xml-to-csharp-classes/comment-page-1/
It seems that you poorly designed your data classes. Try solution below.
public class OtherGenericProperties
{
[XmlElement("Data")]
public string Data { get; set; }
}
[XmlRoot("documents")]
public class Documents
{
[XmlElement("document")]
public Document Document { get; set; }
}
public class Document
{
[XmlElement("Keys")] // this is used so the destination name can change in the future
public Keys Keys { get; set; }
[XmlElement("otherGenericProperties")]
public OtherGenericProperties OtherGenericProperties { get; set; }
[XmlElement("Repeat")] // This does not work
public List<Repeat> ListRepeat { get; set; }
}
public class Keys
{
[XmlElement("drawer")]
public string Drawer { get; set; }
[XmlElement("somedata")]
public string SomeData { get; set; }
}
public class Repeat
{
[XmlElement("FileInfo")]
public FileInfo FileInfo { get; set; }
}
public class FileInfo
{
[XmlAttribute("mimeType")]
public string MimeType { get; set; }
[XmlAttribute("HREF")]
public string Href { get; set; }
}
Serializing:
internal static void Test()
{
var doc = new Documents
{
Document = new Document
{
Keys = new Keys
{
Drawer = "GraphicData",
SomeData = "otherData"
},
OtherGenericProperties = new OtherGenericProperties { Data = "GenericData 2" },
ListRepeat = new List<Repeat>
{
new Repeat { FileInfo =new FileInfo { Href = "PdfFile.pdf", MimeType = "application/pdf" } },
new Repeat { FileInfo = new FileInfo { Href = "PdfFile2.pdf", MimeType = "application/pdf" } }
}
}
};
XmlSerializer serializer = new XmlSerializer(typeof(Documents));
using (var f = new StreamWriter("D:\\doc.xml", false, Encoding.GetEncoding("Windows-1252")))
{
serializer.Serialize(f, doc);
f.Flush();
}
}
Output:
<?xml version="1.0" encoding="Windows-1252"?>
<documents xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<document>
<Keys>
<drawer>GraphicData</drawer>
<somedata>otherData</somedata>
</Keys>
<otherGenericProperties>
<Data>GenericData 2</Data>
</otherGenericProperties>
<Repeat>
<FileInfo mimeType="application/pdf" HREF="PdfFile.pdf" />
</Repeat>
<Repeat>
<FileInfo mimeType="application/pdf" HREF="PdfFile2.pdf" />
</Repeat>
</document>
</documents>

How can I deserialize an XML - Windows Phone

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; }
}

deserialize returns null objects c#

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(...);

Categories