I got an XML response as:
<?xml version='1.0' encoding='UTF-8'?>
<tsResponse xmlns="site.com/api" xmlns:xsi="site.org/2001/XMLSchema-instance" xsi:schemaLocation="site.com/api help.site.com/samples/en-us/rest_api/ts-api_3_4.xsd">
<credentials token="xxxxxx-xxxxxx-xxxxxx">
<site id="xxxxxx-xxxxxx-xxxxxx" contentUrl="sitename"/>
<user id="xxxxxx-xxxxxx-xxxxxx"/>
</credentials>
</tsResponse>
How can I parse this response to get the token value inside credentials?
By using LINQ to XML.
c#
void Main()
{
XDocument xdoc = XDocument.Parse(#"<tsResponse xmlns='site.com/api' xmlns:xsi='site.org/2001/XMLSchema-instance' xsi:schemaLocation='site.com/api help.site.com/samples/en-us/rest_api/ts-api_3_4.xsd'>
<credentials token='xxxxxx-xxxxxx-xxxxxx'>
<site id='xxxxxx-xxxxxx-xxxxxx' contentUrl='sitename'/>
<user id='xxxxxx-xxxxxx-xxxxxx'/>
</credentials>
</tsResponse>");
XNamespace ns1 = xdoc.Root.GetDefaultNamespace();
string token = xdoc.Descendants(ns1 + "credentials")
.FirstOrDefault()?.Attribute("token").Value;
Console.WriteLine("token = {0}", token);
}
Output
token = xxxxxx-xxxxxx-xxxxxx
I think that the simplest way is to use LINQ to XML's XDocument:
using System.Xml;
using System.Xml.Linq;
/* ... */
var xml = #"<?xml version='1.0' encoding='UTF-8'?>
<tsResponse xmlns=""site.com/api"" xmlns:xsi=""site.org/2001/XMLSchema-instance"" xsi:schemaLocation=""site.com/api help.site.com/samples/en-us/rest_api/ts-api_3_4.xsd"">
<credentials token=""xxxxxx-xxxxxx-xxxxxx"">
<site id=""xxxxxx-xxxxxx-xxxxxx"" contentUrl=""sitename""/>
<user id=""xxxxxx-xxxxxx-xxxxxx""/>
</credentials>
</tsResponse>";
var xmlns = XNamespace.Get("site.com/api");
var token = XDocument.Parse(xml)
.Element(xmlns + "tsResponse")?
.Element(xmlns + "credentials")?
.Attribute("token")?
.Value;
If you put the XML string that you get into the variable xmlToParse, you can do:
TsResponse result = _xmlParser.Deserialize<TsResponse>(xmlToParse);
Where _xmlParser is an instance of the class:
using System.Xml.Serialization;
namespace yourNameSpace
{
public class XmlParser
{
public T Deserialize<T>(string input) where T : class
{
XmlSerializer ser = new XmlSerializer(typeof(T));
using (StringReader sr = new StringReader(input))
{
return (T)ser.Deserialize(sr);
}
}
}
}
You also need to model the whole XML object as a class in C# (XmlObjects.Credentials in my example) using the for example the page https://json2csharp.com/code-converters/xml-to-csharp which gives you:
// using System.Xml.Serialization;
// XmlSerializer serializer = new XmlSerializer(typeof(TsResponse));
// using (StringReader reader = new StringReader(xml))
// {
// var test = (TsResponse)serializer.Deserialize(reader);
// }
[XmlRoot(ElementName="site")]
public class Site {
[XmlAttribute(AttributeName="id")]
public string Id { get; set; }
[XmlAttribute(AttributeName="contentUrl")]
public string ContentUrl { get; set; }
}
[XmlRoot(ElementName="user")]
public class User {
[XmlAttribute(AttributeName="id")]
public string Id { get; set; }
}
[XmlRoot(ElementName="credentials")]
public class Credentials {
[XmlElement(ElementName="site")]
public Site Site { get; set; }
[XmlElement(ElementName="user")]
public User User { get; set; }
[XmlAttribute(AttributeName="token")]
public string Token { get; set; }
}
[XmlRoot(ElementName="tsResponse")]
public class TsResponse {
[XmlElement(ElementName="credentials")]
public Credentials Credentials { get; set; }
[XmlAttribute(AttributeName="xmlns")]
public string Xmlns { get; set; }
[XmlAttribute(AttributeName="xsi")]
public string Xsi { get; set; }
[XmlAttribute(AttributeName="schemaLocation")]
public string SchemaLocation { get; set; }
}
, then you can go with
result.Credentials.Token
Related
I get from a webservice a XML like this:
<s:Envelope
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<Get_PersonResponse
xmlns="http://tempuri.org/">
<Get_PersonResult
xmlns:a="http://schemas.datacontract.org/example"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:LASTNAME>DOE</a:LASTNAME>
<a:EMAIL/>
<a:FIRSTNAME>JONH</a:FIRSTNAME>
<a:NUM_CARD/>
<a:ID_PERSON>12345456</a:ID_PERSON>
<a:PHONE/>
</Get_PersonResult>
<RESP_COMMENT>"Person found"</RESP_COMMENT>
</Get_PersonResponse>
</s:Body>
</s:Envelope>
I want to check if RESP_COMMENT is empty. Then if is not empty get all the values (a:FIRSTNAME, a:LASTNAME, etc)
I got the a:FIRSTNAME on the fiddle but I am not sure if I have to do a bucle for every single value or is a better way to do it:
https://dotnetfiddle.net/cOsX6s
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(output);
XmlNodeList nodeList = xmldoc.GetElementsByTagName("a:FIRSTNAME");
string lastname = string.Empty;
foreach (XmlNode node in nodeList)
{
firstname = node.InnerText;
Console.Write(firstname);
}
Try xml serialization :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication177
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
string xml = File.ReadAllText(FILENAME);
StringReader sReader = new StringReader(xml);
XmlReader xReader = XmlReader.Create(sReader);
XmlSerializer serializer = new XmlSerializer(typeof(Envelope));
Envelope envelope = (Envelope)serializer.Deserialize(xReader);
}
}
[XmlRoot(ElementName = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope
{
[XmlElement(Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public Body Body { get; set; }
}
public class Body
{
[XmlElement(ElementName = "Get_PersonResponse", Namespace = "http://tempuri.org/")]
public Get_PersonResponse Get_PersonResponse { get; set; }
}
public class Get_PersonResponse
{
[XmlElement(Namespace = "http://tempuri.org/")]
public string RESP_COMMENT { get; set; }
[XmlElement(ElementName = "Get_PersonResult", Namespace = "http://tempuri.org/")]
public Get_PersonResult Get_PersonResult { get; set; }
}
public class Get_PersonResult
{
[XmlElement(Namespace = "http://schemas.datacontract.org/example")]
public string LASTNAME { get;set;}
[XmlElement(Namespace = "http://schemas.datacontract.org/example")]
public string EMAIL { get; set; }
[XmlElement(Namespace = "http://schemas.datacontract.org/example")]
public string FIRSTNAME { get; set; }
[XmlElement(Namespace = "http://schemas.datacontract.org/example")]
public string NUM_CARD { get; set; }
[XmlElement(Namespace = "http://schemas.datacontract.org/example")]
public string ID_PERSON { get; set; }
[XmlElement(Namespace = "http://schemas.datacontract.org/example")]
public string PHONE { get; set; }
}
}
There should be a way to get this values using xpath. I have tried a few different things and it seems that the empty prefix on namespace xmlns="http://tempuri.org/" is causing problems, see the element Get_PersonResponse namespace attribute.
<Get_PersonResponse xmlns="http://tempuri.org/">
To overcome this problem we need to remove the namespace that is causing the problem before loading the xml.
using System;
using System.Xml;
public class Program
{
public static void Main()
{
string output = #"<s:Envelope
xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
<s:Body>
<Get_PersonResponse
xmlns=""http://tempuri.org/"">
<Get_PersonResult
xmlns:a=""http://schemas.datacontract.org/example""
xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
<a:LASTNAME>DOE</a:LASTNAME>
<a:EMAIL/>
<a:FIRSTNAME>JONH</a:FIRSTNAME>
<a:NUM_CARD/>
<a:ID_PERSON>12345456</a:ID_PERSON>
<a:PHONE/>
</Get_PersonResult>
<RESP_COMMENT>""Person found!""</RESP_COMMENT>
</Get_PersonResponse>
</s:Body>
</s:Envelope>";
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(output.Replace(#"xmlns=""http://tempuri.org/""", ""));
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmldoc.NameTable);
nsmgr.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
nsmgr.AddNamespace("a", "http://schemas.datacontract.org/example");
var personResultNode = xmldoc.SelectSingleNode("//s:Envelope/s:Body/Get_PersonResponse/Get_PersonResult", nsmgr);
foreach (var nodes in personResultNode.ChildNodes)
{
var personAttribute = (XmlNode)nodes;
Console.WriteLine($"{personAttribute.LocalName}: {personAttribute.InnerText}");
}
Console.Read();
}
}
BTW: I have also tried to add an empty prefix for namespace xmlns="http://tempuri.org/" to the XmlNamespaceManager but that didn't help.
I'm trying to not use the old way of reading xml and give a try to linq to xml but I'm having a hard time to accomplish the following:
Response XML
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<PolicyResponse>
<serviceResponse>
<responseCode>0</responseCode>
<responseDescription>Success</responseDescription>
<responseDetail>Success</responseDetail>
<paging>
<pageNum>1</pageNum>
<pageSize>3</pageSize>
<totalRecords>3</totalRecords>
</paging>
</serviceResponse>
<detailsResponseList>
<detailResponse>
<policy>
<policySummaryInfo>
<PolicyNumber>1199128</PolicyNumber>
<PremiumChangeAmt>...</PremiumChangeAmt>
<WrittenAmt>...</WrittenAmt>
<PolicyStatusDesc>Expired</PolicyStatusDesc>
<BillingInfo>
<InsuredOrPrincipal>...</InsuredOrPrincipal>
</BillingInfo>
</policySummaryInfo>
</policy>
</detailResponse>
<detailResponse>
<policy>
<policySummaryInfo>
<PolicyNumber>1199128</PolicyNumber>
<PremiumChangeAmt>...</PremiumChangeAmt>
<WrittenAmt>...</WrittenAmt>
<PolicyStatusDesc>Active</PolicyStatusDesc>
<BillingInfo>
<InsuredOrPrincipal>...</InsuredOrPrincipal>
</BillingInfo>
</policySummaryInfo>
</policy>
</detailResponse>
</detailsResponseList>
</PolicyResponse>
</out2:getPolicySummaryResponse>
</soapenv:Body>
</soapenv:Envelope>
I'm doing this in C#
XDocument xdoc = new XDocument();
xdoc = XDocument.Parse(xmlResponse);
IEnumerable<XElement> items =
from el in xdoc.Descendants("detailResponse")
select el;
With this code I'm able to read bot detailReponse element but what I want is to keep only the XML detailResponse structure with the <PolicyStatusDesc></PolicyStatusDesc> equal to Active. Is that possible. I manage to gather specific data based on element names an so but how I can keep the whole XML child element structure (detailResponse element an its childs) by using Linq to XML.
Thanks
In your XML sample there in an invalid
</out2:getPolicySummaryResponse> tag which i have removed
Here is the code you may use for your result
IEnumerable<XElement> items =
from el in xdoc.Descendants("detailResponse")
where el.Element("policy")?.Element("policySummaryInfo")?.Element("PolicyStatusDesc")?.Value == "Active"
select el;
I often use code like below which gives a flatter results than using Serialization:
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);
XElement xPolicyResponse = doc.Descendants("PolicyResponse").FirstOrDefault();
PolicyResponse policyResponse = new PolicyResponse(xPolicyResponse);
}
}
public class PolicyResponse
{
public ServiceResponse serviceResponse { get; set; }
public List<DetailResponse> detailResponse { get; set; }
public PolicyResponse() {}
public PolicyResponse(XElement element)
{
XElement xServiceResponse = element.Element("serviceResponse");
List<XElement> xdetailResponseList = element.Descendants("detailResponse").ToList();
serviceResponse = new ServiceResponse(xServiceResponse);
detailResponse = xdetailResponseList.Select(x => new DetailResponse(x)).ToList();
}
}
public class ServiceResponse
{
public int responseCode { get; set; }
public string responseDescription { get; set; }
public string responseDetail { get; set; }
public int pageNum { get; set; }
public int pageSize { get; set; }
public int totalRecords { get; set; }
public ServiceResponse() { }
public ServiceResponse(XElement element)
{
responseCode = (int)element.Element("responseCode");
responseDescription = (string)element.Element("responseDescription");
responseDetail = (string)element.Element("responseDetail");
pageNum = (int)element.Descendants("pageNum").FirstOrDefault();
pageSize = (int)element.Descendants("pageSize").FirstOrDefault();
totalRecords = (int)element.Descendants("totalRecords").FirstOrDefault();
}
}
public class DetailResponse
{
public string policyNumber { get; set; }
public string premiumChangeAmt { get; set; }
public string writtenAmt { get; set; }
public string policyStatusDesc { get; set; }
public string insuredOrPrincipal { get; set; }
public DetailResponse() { }
public DetailResponse(XElement element)
{
XElement xPolicySummaryInfo = element.Descendants("policySummaryInfo").FirstOrDefault();
policyNumber = (string)xPolicySummaryInfo.Element("PolicyNumber");
premiumChangeAmt = (string)xPolicySummaryInfo.Element("PremiumChangeAmt");
writtenAmt = (string)xPolicySummaryInfo.Element("WrittenAmt");
policyStatusDesc = (string)xPolicySummaryInfo.Element("PolicyStatusDesc");
insuredOrPrincipal = (string)xPolicySummaryInfo.Descendants("InsuredOrPrincipal").FirstOrDefault();
}
}
}
You can get the elements where <PolicyStatusDesc> has the value Active by filtering the <detailResponse> elements using el.Descendants("PolicyStatusDesc"), then selecting those elements having the value Active.
Using query syntax:
var items = from el in xdoc.Descendants("detailResponse")
where el.Descendants("PolicyStatusDesc").Any(p => p.Value == "Active")
select el;
Or using method syntax:
var items = xdoc.Descendants("detailResponse")
.Where(el => el.Descendants("PolicyStatusDesc")
.Any(p => p.Value == "Active"));
I'm currently working on an ASP.NET MVC 4.6 application using SQL Server 2014 as a data storage.
I need to parse a XML document from an URL, transform the XML into an object and store it into the database using Entity Framework 6.
I need to parse the XML from an URL like this:
http: //api.myserver.com/notes.xml
My XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<notes>
<note>
<to>Tove</to>
<from>Jane</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
<note>
<to>Doe</to>
<from>John</from>
<heading>Meeting</heading>
<body>Hello Monday!</body>
</note>
<notes>
My model class note looks like this:
public class Note
{
public string To { get; set; }
public string From { get; set; }
public string Heading { get; set; }
public string Body { get; set; }
}
My current test implementation looks like this, BUT I'm not so happy about the parsing of the XML:
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
String url = "https://www.w3schools.com/xml/note.xml";
XmlDocument doc = new XmlDocument();
doc.Load(url);
XmlElement root = doc.DocumentElement;
StringBuilder sb = new StringBuilder();
sb.Append("<note>");
foreach (XmlNode item in root)
{
sb.Append(item.OuterXml);
}
sb.Append("</note>");
var result = Deserialize<Note>(sb.ToString());
// further processing of result using EF 6
Console.ReadKey();
}
public static T Deserialize<T>(string xmlText)
{
try
{
var stringReader = new StringReader(xmlText);
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
catch (Exception ex)
{
throw;
}
}
}
[XmlRoot(ElementName = "note", Namespace = "")]
public class Note
{
[XmlElement("to")]
public string To { get; set; }
[XmlElement("from")]
public string From { get; set; }
[XmlElement("heading")]
public string Heading { get; set; }
[XmlElement("body")]
public string Body { get; set; }
}
}
As far as I noticed, .NET provides several approaches on how to parse XML:
XmlReader Class
XPath
XDocument
DataSet
LINQ
I was wondering, what would you use and how would u solve the parsing of the XML document from an URL using .NET and converting into an object to store it in a SQL Server DB?
Thanks for your help and consideration!!
This might help you, a serialize method and a Deserialize method.
public static string Serialize<T>(T dataToSerialize)
{
try
{
var stringwriter = new ISOEncodingStringWriter();
var serializer = new XmlSerializer(typeof(T));
var xns = new XmlSerializerNamespaces();
xns.Add(string.Empty, string.Empty);
serializer.Serialize(stringwriter, dataToSerialize, xns);
return stringwriter.ToString();
}
catch
{
throw;
}
}
public static T Deserialize<T>(string xmlText)
{
try
{
var stringReader = new System.IO.StringReader(xmlText);
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
catch
{
throw;
}
}
You use the method like this:
Note result = Deserialize<Note>(xmlstring);
Note note = new Note(){...};
string convertedToXML = Serialize<Note>(note);
Just remember that you need to add some data to your note class so it can actually serialize the data:
[XmlRoot(ElementName = "root", Namespace = "")]
public class Note
{
[XmlElementAttribute("To")]
public string To { get; set; }
[XmlElementAttribute("From")]
public string From { get; set; }
[XmlElementAttribute("Heading")]
public string Heading { get; set; }
[XmlElementAttribute("Body")]
public string Body { get; set; }
}
I hope it helps :)
You can use LINQ to XML to traverse the nodes of your documents and either EF or ADO.NET to store them in the DB.
You can start with this helpful tutorial: http://www.c-sharpcorner.com/UploadFile/de41d6/learning-linq-made-easy-tutorial-1/
I am having a problem with deserialization of an array which comes from the SOAP response. It returns null, but in response I can actually see a correct xml message. Maybe someone can see what's wrong in my code. Thanks in advance.
SOAP response:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns-wp="imcwp" xmlns:ns-hostax="imchostax" xmlns:ns-ilms="imcilms" xmlns:ns-qtms="imcqtms" xmlns:ns-tptms="imctptms">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns-wp:strTrailerRequest-TrailerResponse>
<ns-wp:Trailer>
<ns-wp:TrailerId>T001</ns-wp:TrailerId>
<ns-wp:TrailerType>Flat Extender</ns-wp:TrailerType>
</ns-wp:Trailer>
<ns-wp:Trailer>
<ns-wp:TrailerId>T002</ns-wp:TrailerId>
<ns-wp:TrailerType>Flat Extender</ns-wp:TrailerType>
</ns-wp:Trailer>
<ns-wp:Trailer>
<ns-wp:TrailerId>T003</ns-wp:TrailerId>
<ns-wp:TrailerType>Flat Extender</ns-wp:TrailerType>
</ns-wp:Trailer>
</ns-wp:strTrailerRequest-TrailerResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Response Model:
[XmlRoot(ElementName = "strTrailerRequest-TrailerResponse", Namespace = "imcwp")]
public class strTrailerRequestTrailerResponse
{
[XmlArray("strTrailerRequest-TrailerResponse", Namespace = "imcwp")]
[XmlArrayItem("Trailer", Namespace = "imcwp")]
public List<Trailer> Trailers { get; set; }
}
Parse Method:
private strTrailerRequestTrailerResponse ParseTrailerResponse(string response)
{
var soap = XDocument.Parse(response);
XNamespace ns = "imcwp";
var trailerResponseNode = soap.Descendants(ns + "strTrailerRequest-TrailerResponse").FirstOrDefault().ToString();
var result = Deserialize<strTrailerRequestTrailerResponse>(trailerResponseNode);
return result;
}
Why not just deserilize the whole object, in that case u dont need xDocument and querying:
var envelop = Deserialize<Envelope>(response);
foreach (var strTrailerRequestTrailerResponseTrailer in envelop.Body.strTrailerRequestTrailerResponse)
{
}
and yr Deserialize method:
public static T Deserialize<T>(string response)
{
var serializer = new XmlSerializer(typeof(T));
using(TextReader reader = new StringReader(response))
{
return (T)serializer.Deserialize(reader);
}
}
If you still want to go with yr way using XDocument, it's ok yr Deserialize method should the same as I defined. if you wont try:
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "imcwp")]
public partial class strTrailerRequestTrailerResponseTrailer
{
public string TrailerId { get; set; }
public string TrailerType { get; set; }
}
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "imcwp")]
[System.Xml.Serialization.XmlRootAttribute("strTrailerRequest-TrailerResponse", Namespace = "imcwp", IsNullable = false)]
public partial class strTrailerRequestTrailerResponse
{
[System.Xml.Serialization.XmlElementAttribute("Trailer")]
public strTrailerRequestTrailerResponseTrailer[] Trailer { get; set; }
}
For simple xml you can use xml linq. See code below :
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace Certificate
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
string xml = File.ReadAllText(FILENAME);
XDocument doc = XDocument.Parse(xml);
XElement soap = doc.Root;
XNamespace ns = soap.GetNamespaceOfPrefix("ns-wp");
List<Trailer> trailers = doc.Descendants(ns + "Trailer").Select(x => new Trailer()
{
trailerId = (string)x.Element(ns + "TrailerId"),
trailerType = (string)x.Element(ns + "TrailerType")
}).ToList();
}
}
public class Trailer
{
public string trailerId { get; set; }
public string trailerType { get;set;}
}
}
I'm trying to convert the XML data to an object list, but it throws an error.
XML
<?xml version="1.0" encoding="utf-8" ?>
<Servers>
<Server>
<ServerName>STAGING</ServerName>
<ServerIP>XXX.XXX.XX.X</ServerIP>
</Server>
</Servers>
C#
public class ServerDetails
{
public string ServerName { get; set; }
public string ServerIP { get; set; }
}
private void GetXMLData()
{
XmlSerializer serializer = new XmlSerializer(typeof(List<ServerDetails>));
using (FileStream stream = File.OpenRead("D:\\Resource.xml"))
{
List<ServerDetails> list = (List<ServerDetails>)serializer.Deserialize(stream);
//Exception here
}
}
ERROR
Inner Exception : <Servers xmlns=''> was not expected.
There is an error in XML document (2,2)
I tried adding the [Serializabe] and [XMLElement] attributes to the class,
and also xmlns="http://www.example.com/xsd/ServerDetails" in the XML
but that did not help.
You have ServerDetails as your class name and in the xml the tag name is different, Try something like this.
public class ServerDetails
{
public string ServerName { get; set; }
public string ServerIP { get; set; }
}
public class ServerList
{
[XmlArray("Servers")]
[XmlArrayItem("Server", Type = typeof(ServerDetails))]
public ServerDetails[] Servers { get;set;}
}
private void GetXMLData()
{
XmlSerializer serializer = new XmlSerializer(typeof(ServerList));
using (FileStream stream = File.OpenRead("D:\\Resource.xml"))
{
var list = (ServerList)serializer.Deserialize(stream);
//Exception here
}
}
I used to use XmlSerializer a lot, but I totally stopped using it because you are forced to create your object structure fitting the xml structure. That makes it hard to maintain. Also XmlSerializer has some serious memory leaks.
If you don't mind, I would suggest to switch to XElement
public IEnumerable<ServerDetails> GetServers(string file)
{
using (var stream = File.Open(file, FileMode.Open, FileAccess.Read))
return GetServers(stream);
}
public IEnumerable<ServerDetails> GetServers(Stream stream)
{
var root = XElement.Load(stream);
return GetServers(root);
}
public IEnumerable<ServerDetails> GetServers(XElement root)
{
foreach (var server in root.Elements("Server"))
{
yield return new ServerDetails
{
ServerName = (string)server.Element("ServerName"),
ServerIP = (string)server.Element("ServerIP"),
};
}
}
Please note that you have to reference System.Xml.Linq
For your convenience here is a test case.
[TestMethod]
public void CanReadServers()
{
var xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" + #"
<Servers>
<Server>
<ServerName>STAGING</ServerName>
<ServerIP>XXX.XXX.XX.X</ServerIP>
</Server>
</Servers>";
IEnumerable<ServerDetails> servers;
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
servers = GetServers(stream).ToList();
Assert.AreEqual(1, servers.Count());
Assert.AreEqual("STAGING", servers.ElementAt(0).ServerName);
Assert.AreEqual("XXX.XXX.XX.X", servers.ElementAt(0).ServerIP);
}
Use XmlElement Notation to specify the element name.
public class Servers
{
[XmlElement("Server")]
public ServerDetails[] ServersDetails { get; set; }
}
public class ServerDetails
{
public string ServerName { get; set; }
public string ServerIP { get; set; }
}
private void GetXMLData()
{
XmlSerializer serializer = new XmlSerializer(typeof(Servers));
using (FileStream stream = File.OpenRead("D:\\Resource.xml"))
{
Servers list = (Servers)serializer.Deserialize(stream);
//Exception here
}
}