Reading XML files with repeating structures using C# - c#

I've got an XML file which describes connections, i.e.
<?xml version="1.0" encoding="utf-8"?>
<connections>
<connection name="Local server remote">
<ip>192.168.0.7 </ip>
<port>23 </port>
<description>
Remote controlling of that nice & neat box under the table.
</description>
</connection>
<connection name="Far, far away server HTTP access">
<ip>77.32.57.144 </ip>
<port>8080 </port>
<description>
A legend tells of a far, far away country of Russia, and of a server somewhere inside this mysterious country.
</description>
</connection>
</connections>
Is there an easy way to parse that XML file into an object of a class like:
class Connection {
public string Name;
public string IP;
public int Port;
public string Description;
}
?

You could use Linq to XML to read this in:
var xmlDoc = XDocument.Load("XMLFile1.xml");
if(xmlDoc.Root != null)
{
var connections = (xmlDoc.Root.Elements("connection").Select(e => new Connection
{
Description = (string) e.Element("description"),
IP = (string) e.Element("ip"),
Name = (string) e.Element("name"),
Port = (int) e.Element("port")
})).ToList();
}
Probably worth pointing out that your XML file contains an & character which will throw off the XML document load. I was able to run the above code by replacing the ampersand with &

You will have to create a wrapper type to contain the list of connections as it doesn't know what <connections> is, then the rest is taken care of by adding an XmlElement name to each field.
public static void Main(string[] args)
{
var serializer = new XmlSerializer(typeof (ConnectionList));
var connections = ((ConnectionList)serializer.Deserialize(new StreamReader("data.xml"))).Connections;
foreach (var connection in connections)
{
Console.WriteLine(connection.IP);
}
Console.ReadLine();
}
[XmlRoot("connections")]
public class ConnectionList
{
[XmlElement("connection")]
public List<Connection> Connections { get; set; } = new List<Connection>();
}
[XmlType("connection")]
public class Connection
{
[XmlElement("description")] public string Description;
[XmlElement("ip")] public string IP;
[XmlElement("name")] public string Name;
[XmlElement("port")] public int Port;
}
Note: '&' in the XML is an invalid character and must be escaped as &

Try this. Best method
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<Connection> connections = doc.Descendants("connection").Select(x => new Connection(){
Name = x.Attribute("name").Value,
IP = x.Element("ip").Value,
Port = int.Parse(x.Element("port").Value),
Description = x.Element("description").Value,
}).ToList();
}
}
public class Connection
{
public string Name { get; set; }
public string IP { get; set; }
public int Port { get; set; }
public string Description { get; set; }
}
}
​

Related

Traversing an XML file using Linq to XML not working as expected

Sorry for the somewhat basic question, but what can I say. I can't figure it out. The problem is that there's a foreach loop that's supposed to iterate through the rows (sections) and while it works for the first section, the second time through the loop it doesn't seem to read the second section. The same data is stored in version. BTW, the way the method is called I would be passing in ProductName as a parameter (There will be multiple products represented here and also a version number (e.g. v2.0.0) that I'll need to filter the results for too.
So I have an XML file that looks like this:
<Products>
<ProductName1>
<v2.0.0>
<GUID>"{B5ECEC43-5406-4E4D-96D9-456823100313}"</GUID>
<VersionNameToUninstall>"2.0.0 - 2.0.2"</VersionNameToUninstall>
<UninstallResponseFile>"GVQC-Client-2.0.0-Uninst.iss"</UninstallResponseFile>
</v2.0.0>
<v2.0.3>
<GUID>"{1D6C02D7-8E87-43BE-8AB2-1FF0E5ACD410}"</GUID>
<VersionNameToUninstall>"2.0.3"</VersionNameToUninstall>
<UninstallResponseFile>"GVQC-Client-2.0.3-Uninst.iss"</UninstallResponseFile>
</v2.0.3>
</ProductName1>
<ProductName2>
<v3.0.0>
<GUID>"{ABCDEC43-5406-4E4D-96D9-456823101234}"</GUID>
<VersionNameToUninstall>"2.2.0 - 2.2.2"</VersionNameToUninstall>
<UninstallResponseFile>"GVQC-Client-2.2.0-Uninst.iss"</UninstallResponseFile>
</v3.0.0>
<v4.0.0>
<GUID>"{5D6C02D7-8E87-43BE-8AB2-1FF0E5ACD589}"</GUID>
<VersionNameToUninstall>"4.0.0"</VersionNameToUninstall>
<UninstallResponseFile>"GVQC-Client-4.0.0-Uninst.iss"</UninstallResponseFile>
</v4.0.0>
</ProductName2>
</Products>
There will only be 10 or so versions (e.g. v2.x.x) so there's not a lot of data here. So I created a multidimensional (nested) class/struct to hold the data and when I try my code to read the data it's not working.
Here are the classes/stucts (I've tried both and neither works) that I'm trying to populate:
public class TopLevelObject
{
public string Version { get; set; }
public RowLevelObject Row {get;set;}
}
public struct RowLevelObject
{
public string Guid { get; set; }
public string VersionName { get; set; }
public string UninstallFileName { get; set; }
}
So here's my code. Please just ignore the Stream - that's so I can embed this XML file in the .exe and not have it be a separate file:
public static List<TopLevelObject> GetGUIDSFromFile(string GUIDKey)
List<InstallScriptMSIXMLTopLevelObject> installScriptMSIXMLTopLevelObjectList = new List<InstallScriptMSIXMLTopLevelObject>();
Stream GUIDXmlFileStream = typeof(PGCommonCA).Assembly.GetManifestResourceStream("PGCommonCA.ProductGUIDs.xml");
XElement xElement = XElement.Load(GUIDXmlFileStream);
var versions = xElement.Elements(GUIDKey).Descendants();
foreach (var version in versions)
{
TopLevelObject topLevelObject = new TopLevelObject();
RowLevelObject rowLevelObject = new RowLevelObject();
TopLevelObject.Version = version.Name.LocalName;
RowLevelObject.Guid = version.Element("GUID").Value;
RowLevelObject.VersionName = version.Element("VersionNameToUninstall").Value;
RowLevelObject.UninstallFileName = version.Element("UninstallResponseFile").Value;
TopLevelObjectList.Add(topLevelObject);
}
return TopLevelObjectList;
}
I know there are many ways to read XML and my choice doesn't work so I'm looking for another simple solution.
The following works :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement productName = doc.Root;
List<TopLevelObject> top = productName.Elements().Select(x => new TopLevelObject() {
Version = x.Name.LocalName,
Row = new RowLevelObject() {
Guid = (string)x.Element("GUID"),
VersionName = (string)x.Element("VersionNameToUninstall"),
UninstallFileName = (string)x.Element("UninstallResponseFile")
}
}).ToList();
}
}
public class TopLevelObject
{
public string Version { get; set; }
public RowLevelObject Row { get; set; }
}
public struct RowLevelObject
{
public string Guid { get; set; }
public string VersionName { get; set; }
public string UninstallFileName { get; set; }
}
}
I figured it out (many thanks to jdweng!!). Here's the final solution based on the revised XML at the top:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static TopLevelObject GetInfo(string xmlKey)
{
XDocument doc = XDocument.Load(FILENAME);
XElement productName = doc.Root;
List<TopLevelObject> top = productName.Descendants(xmlKey).Elements().Select(x => new TopLevelObject() {
Version = x.Name.LocalName,
Row = new RowLevelObject() {
Guid = (string)x.Element("GUID"),
VersionName = (string)x.Element("VersionNameToUninstall"),
UninstallFileName = (string)x.Element("UninstallResponseFile")
}
}).ToList();
}
}
public class TopLevelObject
{
public string Version { get; set; }
public RowLevelObject Row { get; set; }
}
public struct RowLevelObject
{
public string Guid { get; set; }
public string VersionName { get; set; }
public string UninstallFileName { get; set; }
}
}

Reading an XML and Parsing as Class and Properties [duplicate]

How do I Deserialize this XML document:
<?xml version="1.0" encoding="utf-8"?>
<Cars>
<Car>
<StockNumber>1020</StockNumber>
<Make>Nissan</Make>
<Model>Sentra</Model>
</Car>
<Car>
<StockNumber>1010</StockNumber>
<Make>Toyota</Make>
<Model>Corolla</Model>
</Car>
<Car>
<StockNumber>1111</StockNumber>
<Make>Honda</Make>
<Model>Accord</Model>
</Car>
</Cars>
I have this:
[Serializable()]
public class Car
{
[System.Xml.Serialization.XmlElementAttribute("StockNumber")]
public string StockNumber{ get; set; }
[System.Xml.Serialization.XmlElementAttribute("Make")]
public string Make{ get; set; }
[System.Xml.Serialization.XmlElementAttribute("Model")]
public string Model{ get; set; }
}
.
[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
[XmlArrayItem(typeof(Car))]
public Car[] Car { get; set; }
}
.
public class CarSerializer
{
public Cars Deserialize()
{
Cars[] cars = null;
string path = HttpContext.Current.ApplicationInstance.Server.MapPath("~/App_Data/") + "cars.xml";
XmlSerializer serializer = new XmlSerializer(typeof(Cars[]));
StreamReader reader = new StreamReader(path);
reader.ReadToEnd();
cars = (Cars[])serializer.Deserialize(reader);
reader.Close();
return cars;
}
}
that don't seem to work :-(
How about you just save the xml to a file, and use xsd to generate C# classes?
Write the file to disk (I named it foo.xml)
Generate the xsd: xsd foo.xml
Generate the C#: xsd foo.xsd /classes
Et voila - and C# code file that should be able to read the data via XmlSerializer:
XmlSerializer ser = new XmlSerializer(typeof(Cars));
Cars cars;
using (XmlReader reader = XmlReader.Create(path))
{
cars = (Cars) ser.Deserialize(reader);
}
(include the generated foo.cs in the project)
Here's a working version. I changed the XmlElementAttribute labels to XmlElement because in the xml the StockNumber, Make and Model values are elements, not attributes. Also I removed the reader.ReadToEnd(); (that function reads the whole stream and returns a string, so the Deserialize() function couldn't use the reader anymore...the position was at the end of the stream). I also took a few liberties with the naming :).
Here are the classes:
[Serializable()]
public class Car
{
[System.Xml.Serialization.XmlElement("StockNumber")]
public string StockNumber { get; set; }
[System.Xml.Serialization.XmlElement("Make")]
public string Make { get; set; }
[System.Xml.Serialization.XmlElement("Model")]
public string Model { get; set; }
}
[Serializable()]
[System.Xml.Serialization.XmlRoot("CarCollection")]
public class CarCollection
{
[XmlArray("Cars")]
[XmlArrayItem("Car", typeof(Car))]
public Car[] Car { get; set; }
}
The Deserialize function:
CarCollection cars = null;
string path = "cars.xml";
XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
StreamReader reader = new StreamReader(path);
cars = (CarCollection)serializer.Deserialize(reader);
reader.Close();
And the slightly tweaked xml (I needed to add a new element to wrap <Cars>...Net is picky about deserializing arrays):
<?xml version="1.0" encoding="utf-8"?>
<CarCollection>
<Cars>
<Car>
<StockNumber>1020</StockNumber>
<Make>Nissan</Make>
<Model>Sentra</Model>
</Car>
<Car>
<StockNumber>1010</StockNumber>
<Make>Toyota</Make>
<Model>Corolla</Model>
</Car>
<Car>
<StockNumber>1111</StockNumber>
<Make>Honda</Make>
<Model>Accord</Model>
</Car>
</Cars>
</CarCollection>
You have two possibilities.
Method 1. XSD tool
Suppose that you have your XML file in this location C:\path\to\xml\file.xml
Open Developer Command Prompt
You can find it in Start Menu > Programs > Microsoft Visual Studio 2012 > Visual Studio Tools
Or if you have Windows 8 can just start typing Developer Command Prompt in Start screen
Change location to your XML file directory by typing cd /D "C:\path\to\xml"
Create XSD file from your xml file by typing xsd file.xml
Create C# classes by typing xsd /c file.xsd
And that's it! You have generated C# classes from xml file in C:\path\to\xml\file.cs
Method 2 - Paste special
Required Visual Studio 2012+
Copy content of your XML file to clipboard
Add to your solution new, empty class file (Shift+Alt+C)
Open that file and in menu click Edit > Paste special > Paste XML As Classes
And that's it!
Usage
Usage is very simple with this helper class:
using System;
using System.IO;
using System.Web.Script.Serialization; // Add reference: System.Web.Extensions
using System.Xml;
using System.Xml.Serialization;
namespace Helpers
{
internal static class ParseHelpers
{
private static JavaScriptSerializer json;
private static JavaScriptSerializer JSON { get { return json ?? (json = new JavaScriptSerializer()); } }
public static Stream ToStream(this string #this)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(#this);
writer.Flush();
stream.Position = 0;
return stream;
}
public static T ParseXML<T>(this string #this) where T : class
{
var reader = XmlReader.Create(#this.Trim().ToStream(), new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Document });
return new XmlSerializer(typeof(T)).Deserialize(reader) as T;
}
public static T ParseJSON<T>(this string #this) where T : class
{
return JSON.Deserialize<T>(#this.Trim());
}
}
}
All you have to do now, is:
public class JSONRoot
{
public catalog catalog { get; set; }
}
// ...
string xml = File.ReadAllText(#"D:\file.xml");
var catalog1 = xml.ParseXML<catalog>();
string json = File.ReadAllText(#"D:\file.json");
var catalog2 = json.ParseJSON<JSONRoot>();
The following snippet should do the trick (and you can ignore most of the serialization attributes):
public class Car
{
public string StockNumber { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
[XmlRootAttribute("Cars")]
public class CarCollection
{
[XmlElement("Car")]
public Car[] Cars { get; set; }
}
...
using (TextReader reader = new StreamReader(path))
{
XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
return (CarCollection) serializer.Deserialize(reader);
}
See if this helps:
[Serializable()]
[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
[XmlArrayItem(typeof(Car))]
public Car[] Car { get; set; }
}
.
[Serializable()]
public class Car
{
[System.Xml.Serialization.XmlElement()]
public string StockNumber{ get; set; }
[System.Xml.Serialization.XmlElement()]
public string Make{ get; set; }
[System.Xml.Serialization.XmlElement()]
public string Model{ get; set; }
}
And failing that use the xsd.exe program that comes with visual studio to create a schema document based on that xml file, and then use it again to create a class based on the schema document.
I don't think .net is 'picky about deserializing arrays'. The first xml document is not well formed.
There is no root element, although it looks like there is. The canonical xml document has a root and at least 1 element (if at all). In your example:
<Root> <-- well, the root
<Cars> <-- an element (not a root), it being an array
<Car> <-- an element, it being an array item
...
</Car>
</Cars>
</Root>
try this block of code if your .xml file has been generated somewhere in disk and if you have used List<T>:
//deserialization
XmlSerializer xmlser = new XmlSerializer(typeof(List<Item>));
StreamReader srdr = new StreamReader(#"C:\serialize.xml");
List<Item> p = (List<Item>)xmlser.Deserialize(srdr);
srdr.Close();`
Note: C:\serialize.xml is my .xml file's path. You can change it for your needs.
For Beginners
I found the answers here to be very helpful, that said I still struggled (just a bit) to get this working. So, in case it helps someone I'll spell out the working solution:
XML from Original Question. The xml is in a file Class1.xml, a path to this file is used in the code to locate this xml file.
I used the answer by #erymski to get this working, so created a file called Car.cs and added the following:
using System.Xml.Serialization; // Added
public class Car
{
public string StockNumber { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
[XmlRootAttribute("Cars")]
public class CarCollection
{
[XmlElement("Car")]
public Car[] Cars { get; set; }
}
The other bit of code provided by #erymski ...
using (TextReader reader = new StreamReader(path))
{
XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
return (CarCollection) serializer.Deserialize(reader);
}
... goes into your main program (Program.cs), in static CarCollection XCar() like this:
using System;
using System.IO;
using System.Xml.Serialization;
namespace ConsoleApp2
{
class Program
{
public static void Main()
{
var c = new CarCollection();
c = XCar();
foreach (var k in c.Cars)
{
Console.WriteLine(k.Make + " " + k.Model + " " + k.StockNumber);
}
c = null;
Console.ReadLine();
}
static CarCollection XCar()
{
using (TextReader reader = new StreamReader(#"C:\Users\SlowLearner\source\repos\ConsoleApp2\ConsoleApp2\Class1.xml"))
{
XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
return (CarCollection)serializer.Deserialize(reader);
}
}
}
}
Hope it helps :-)
Kevin's anser is good, aside from the fact, that in the real world, you are often not able to alter the original XML to suit your needs.
There's a simple solution for the original XML, too:
[XmlRoot("Cars")]
public class XmlData
{
[XmlElement("Car")]
public List<Car> Cars{ get; set; }
}
public class Car
{
public string StockNumber { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
And then you can simply call:
var ser = new XmlSerializer(typeof(XmlData));
var data = (XmlData)ser.Deserialize(XmlReader.Create(PathToCarsXml));
One liner:
var object = (Cars)new XmlSerializer(typeof(Cars)).Deserialize(new StringReader(xmlString));
Try this Generic Class For Xml Serialization & Deserialization.
public class SerializeConfig<T> where T : class
{
public static void Serialize(string path, T type)
{
var serializer = new XmlSerializer(type.GetType());
using (var writer = new FileStream(path, FileMode.Create))
{
serializer.Serialize(writer, type);
}
}
public static T DeSerialize(string path)
{
T type;
var serializer = new XmlSerializer(typeof(T));
using (var reader = XmlReader.Create(path))
{
type = serializer.Deserialize(reader) as T;
}
return type;
}
}
How about a generic class to deserialize an XML document
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Generic class to load any xml into a class
// used like this ...
// YourClassTypeHere InfoList = LoadXMLFileIntoClass<YourClassTypeHere>(xmlFile);
using System.IO;
using System.Xml.Serialization;
public static T LoadXMLFileIntoClass<T>(string xmlFile)
{
T returnThis;
XmlSerializer serializer = new XmlSerializer(typeof(T));
if (!FileAndIO.FileExists(xmlFile))
{
Console.WriteLine("FileDoesNotExistError {0}", xmlFile);
}
returnThis = (T)serializer.Deserialize(new StreamReader(xmlFile));
return (T)returnThis;
}
This part may, or may not be necessary. Open the XML document in Visual Studio, right click on the XML, choose properties. Then choose your schema file.
The idea is to have all level being handled for deserialization
Please see a sample solution that solved my similar issue
<?xml version="1.0" ?>
<TRANSACTION_RESPONSE>
<TRANSACTION>
<TRANSACTION_ID>25429</TRANSACTION_ID>
<MERCHANT_ACC_NO>02700701354375000964</MERCHANT_ACC_NO>
<TXN_STATUS>F</TXN_STATUS>
<TXN_SIGNATURE>a16af68d4c3e2280e44bd7c2c23f2af6cb1f0e5a28c266ea741608e72b1a5e4224da5b975909cc43c53b6c0f7f1bbf0820269caa3e350dd1812484edc499b279</TXN_SIGNATURE>
<TXN_SIGNATURE2>B1684258EA112C8B5BA51F73CDA9864D1BB98E04F5A78B67A3E539BEF96CCF4D16CFF6B9E04818B50E855E0783BB075309D112CA596BDC49F9738C4BF3AA1FB4</TXN_SIGNATURE2>
<TRAN_DATE>29-09-2015 07:36:59</TRAN_DATE>
<MERCHANT_TRANID>150929093703RUDZMX4</MERCHANT_TRANID>
<RESPONSE_CODE>9967</RESPONSE_CODE>
<RESPONSE_DESC>Bank rejected transaction!</RESPONSE_DESC>
<CUSTOMER_ID>RUDZMX</CUSTOMER_ID>
<AUTH_ID />
<AUTH_DATE />
<CAPTURE_DATE />
<SALES_DATE />
<VOID_REV_DATE />
<REFUND_DATE />
<REFUND_AMOUNT>0.00</REFUND_AMOUNT>
</TRANSACTION>
</TRANSACTION_RESPONSE>
The above XML is handled in two level
[XmlType("TRANSACTION_RESPONSE")]
public class TransactionResponse
{
[XmlElement("TRANSACTION")]
public BankQueryResponse Response { get; set; }
}
The Inner level
public class BankQueryResponse
{
[XmlElement("TRANSACTION_ID")]
public string TransactionId { get; set; }
[XmlElement("MERCHANT_ACC_NO")]
public string MerchantAccNo { get; set; }
[XmlElement("TXN_SIGNATURE")]
public string TxnSignature { get; set; }
[XmlElement("TRAN_DATE")]
public DateTime TranDate { get; set; }
[XmlElement("TXN_STATUS")]
public string TxnStatus { get; set; }
[XmlElement("REFUND_DATE")]
public DateTime RefundDate { get; set; }
[XmlElement("RESPONSE_CODE")]
public string ResponseCode { get; set; }
[XmlElement("RESPONSE_DESC")]
public string ResponseDesc { get; set; }
[XmlAttribute("MERCHANT_TRANID")]
public string MerchantTranId { get; set; }
}
Same Way you need multiple level with car as array
Check this example for multilevel deserialization
If you're getting errors using xsd.exe to create your xsd file, then use the XmlSchemaInference class as mentioned on msdn. Here's a unit test to demonstrate:
using System.Xml;
using System.Xml.Schema;
[TestMethod]
public void GenerateXsdFromXmlTest()
{
string folder = #"C:\mydir\mydata\xmlToCSharp";
XmlReader reader = XmlReader.Create(folder + "\some_xml.xml");
XmlSchemaSet schemaSet = new XmlSchemaSet();
XmlSchemaInference schema = new XmlSchemaInference();
schemaSet = schema.InferSchema(reader);
foreach (XmlSchema s in schemaSet.Schemas())
{
XmlWriter xsdFile = new XmlTextWriter(folder + "\some_xsd.xsd", System.Text.Encoding.UTF8);
s.Write(xsdFile);
xsdFile.Close();
}
}
// now from the visual studio command line type: xsd some_xsd.xsd /classes
You can just change one attribute for you Cars car property from XmlArrayItem to XmlElment. That is, from
[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
[XmlArrayItem(typeof(Car))]
public Car[] Car { get; set; }
}
to
[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
[XmlElement("Car")]
public Car[] Car { get; set; }
}
My solution:
Use Edit > Past Special > Paste XML As Classes to get the class in your code
Try something like this: create a list of that class (List<class1>), then use the XmlSerializer to serialize that list to a xml file.
Now you just replace the body of that file with your data and try to deserialize it.
Code:
StreamReader sr = new StreamReader(#"C:\Users\duongngh\Desktop\Newfolder\abc.txt");
XmlSerializer xml = new XmlSerializer(typeof(Class1[]));
var a = xml.Deserialize(sr);
sr.Close();
NOTE: you must pay attention to the root name, don't change it. Mine is "ArrayOfClass1"

Get data from a XML-Node by element-name

I´ve got a class called "server" with all attributes. I want to fill in the data from each node/element into the class.
The only way I know is foreach and than everytime a big switch-case. This can´t be the best way!
Here the XML-File:
<serverData .....>
<name>...</name>
<number>...</number>
<language>de</language>
<timezone>...</timezone>
<domain>...</domain>
<version>...</version>
...
</serverData>
The XML-File is from an API and I get it with this lines:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(Request.request(URL));
And now I want do something like (no real code just an example):
Server server = new Server();
server.name = xmlDoc.node["name"].Value;
server.version = ...
...
Thank you for your solution.
You can use LINQ to XML:
XDocument xDoc = XDocument.Parse(Request.request(URL));
Server server = new Server {
name = xDoc.Root.Element("name").Value,
number = int.Parse(xDoc.Root.Element("name").Value),
language = xDoc.Root.Element("language").Value,
timezone = xDoc.Root.Element("timezone").Value
/* etc. */
};
Since you have a well-formatted XML file with a constant structure, you can also simply serialize it using XmlSerializer:
[Serializable]
[XmlRoot("serverData")]
public class ServerData
{
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("number")]
public int Number { get; set; }
[XmlElement("language")]
public string Language { get; set; }
[XmlElement("timezone")]
public string Timezone { get; set; }
/* ... */
}
XmlSerializer xmlSerializer = new XmlSerializer(typeof(ServerData));
using (Stream s = GenerateStreamFromString(Request.request(URL)))
{
xmlSerializer.Deserialize(s);
}
GenerateStreamFromString implementation can be found here.

XML DeSerialize - Possible to Trap Extra XML in File?

Is there a way to trap the extra XML tags in a file that you did not anticipate in your class?
For Example:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace XmlDeserializerTest
{
class Program
{
static void Main(string[] args)
{
XmlSerializer deserializer = new XmlSerializer(typeof(PersonInfo));
StreamReader reader = new StreamReader(#"C:\XML\Xml.xml");
object obj = deserializer.Deserialize(reader);
PersonInfo D = (PersonInfo) obj;
Console.WriteLine(D.address.Age);
reader.Close();
Console.ReadLine();
}
}
[XmlRoot("MyInfo")]
public class PersonInfo
{
public string Name { get; set; }
public string Type { get; set; }
[XmlElement("Address")]
public Loc address = new Loc();
}
public class Loc
{
public string Age { get; set; }
public string Location { get; set; }
}
// File used by this program:
// <?xml version="1.0" encoding="utf-8"?>
// <MyInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
// <Address>
// <Age>51</Age>
// <Location>Tulsa</Location>
// <State>Oklahoma</State>
// </Address>
// <Name>Scott</Name>
// <Type>Programmer</Type>
//</MyInfo>
}
This does not produce an error, it just doesnt load the State information. It just ignores it. I was wondering if there was a way to Trap this or send the extra code to another class or something.
Thanks,
Scott
I believe you can use XmlSerializer's UnknownAttribute, UnknownElement, etc. events to trap such cases.

retrieve table from a web service to wp7 page

Is there any way to retrieve a table from a web service to a wp7 app page as its?
i just wanna sth easy to use than making a table each time getting data
Have look on MSDN for System.Xml namespace. It contains lot of usable classes. You should use XmlReader or something similair to load it to array or generic collection. Hope this helps, 'cause bit unclear question.
EDIT:
This is the code what have I done for loading sample data to generic collection:
Sample data:
<?xml version="1.0" encoding="utf-8" ?>
<studentPunishmentsTables>
<studentPunishmentsTable>
<fromSemester/>
<fromSemesterDesc/>
<issueDate>01/04/2012</issueDate>
<note/>
<penalty>Course Failure</penalty>
<semester>311</semester>
<semesterDesc>First Semester 31/32</semesterDesc>
<toSemester/>
<toSemesterDesc/>
</studentPunishmentsTable>
<studentPunishmentsTable>
<fromSemester/>
<fromSemesterDesc/>
<issueDate>01/04/2012</issueDate>
<note/>
<penalty>Semester Failure</penalty>
<semester>311</semester>
<semesterDesc>First Semester 31/32</semesterDesc>
<toSemester/>
<toSemesterDesc/>
</studentPunishmentsTable>
</studentPunishmentsTables>
Code:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
namespace XMLStudent
{
class Program
{
static void Main(string[] args)
{
XmlDocument doc = new XmlDocument();
doc.Load("st.xml");
List<StudentPunishment> sp = new List<StudentPunishment>();
foreach (XmlNode nod in doc.SelectNodes(#"studentPunishmentsTables/studentPunishmentsTable"))
{
StudentPunishment s = new StudentPunishment();
s.FromSemester = nod.ChildNodes[0].InnerText;
s.FromSemesterDesc = nod.ChildNodes[1].InnerText;
s.IssueDate = nod.ChildNodes[2].InnerText;
s.Note = nod.ChildNodes[3].InnerText;
s.Penalty = nod.ChildNodes[4].InnerText;
s.Semester = nod.ChildNodes[5].InnerText;
s.SemesterDesc = nod.ChildNodes[6].InnerText;
s.ToSemester = nod.ChildNodes[7].InnerText;
s.ToSemesterDesc = nod.ChildNodes[8].InnerText;
sp.Add(s);
}
Console.WriteLine(sp[0].IssueDate);
Console.Read();
}
}
class StudentPunishment
{
public string FromSemester { get; set; }
public string FromSemesterDesc { get; set; }
public string IssueDate { get; set; }
public string Note { get; set; }
public string Penalty { get; set; }
public string Semester { get; set; }
public string SemesterDesc { get; set; }
public string ToSemester { get; set; }
public string ToSemesterDesc { get; set; }
}
}
So. This code is loading XMLDocument with sample data and it's selecting data of each studentPunishmentsTable into new object of StudentPunishment class. There're properties for holding that data. After everything is done and object of student's punishment is added into generic collection ('List'), code is trying to show date of first object in collection. You can test is yourself, it's working for me.

Categories

Resources