I'm calling a WebService with HttpWebRequest.Create method and read it with StreamReader, (below method do this job):
private string ReadWebMethod(string address)
{
var myRequest = (HttpWebRequest)HttpWebRequest.Create(address);
myRequest.Method = "POST";
using (var responseReader = new StreamReader(myRequest.GetResponse().GetResponseStream()))
return responseReader.ReadToEnd();
}
This method works well and output a string like this:
<ArrayOfAppObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
<AppObject>
<Name>MyApp</Name>
<Image>StoreApp.png</Image>
<Version>SM2.1.0</Version>
</AppObject>
</ArrayOfAppObject>
Now I want to have a look in this string, So I use XElemnet and parse the string. (below code):
XElement x = XElement.Parse(ReadWebMethod(address));
Now, When I foreach, x.Elements("AppObject"), it doesnt return any item and skip the foreach.
My foreach is like this:
foreach (var item in x.Elements("AppObject"))
{
listApplication.Add(new AppObject { Image = item.Element("Image").Value, Name = item.Element("Name").Value, Version = item.Element("Version").Value });
}
I create a local string and remove attributes after "ArrayOfAppObject" (xmlns:xsi="htt...)(some where name it xmlnamespace) and test it again, And it works as well and foreach does not skipped!
SO, How can I use the xml with namespace?
use XDocument class
using System.Xml.Linq;
//...
var xml = ReadWebMethod(address);
var xdocument = System.Xml.Linq.XDocument.Parse(xml);
updates
as your XML data define the namespace.. xmlns="http://tempuri.org/"
You must declare full XName with valid namespace. to access each element value
XName theElementName = XName.Get("AppObject", "http://tempuri.org/");
//or alternate way..
XName theElementName = XName.Get("{http://tempuri.org/}AppObject");
here 's sample test method
[TestMethod]
public void ParseXmlElement()
{
XDocument xdoc = XDocument.Parse(this.mockXml);
XName appTag = XName.Get("{http://tempuri.org/}AppObject");
XName nameTag = XName.Get("{http://tempuri.org/}Name");
XName imageTag = XName.Get("{http://tempuri.org/}Image");
XElement objElement = xdoc.Root.Element(appTag);
Assert.IsNotNull(objElement, "<AppObject> not found");
Assert.AreEqual("{http://tempuri.org/}AppObject", objElement.Name);
XElement nameElement = objElement.Element(nameTag);
Assert.IsNotNull(nameElement, "<Name> not found");
Assert.AreEqual("MyApp", nameElement.Value);
XElement imageElement = objElement.Element(imageTag);
Assert.IsNotNull(imageElement, "<Image> not found");
Assert.AreEqual("StoreApp.png", imageElement.Value);
}
using Xml.Linq this way..
[TestMethod]
public void ParseXmlLinq()
{
XDocument xdoc = XDocument.Parse(this.mockXml);
XElement app = xdoc.Root.Elements()
.FirstOrDefault(e => e.Name == XName.Get("AppObject", "http://tempuri.org/"));
Assert.IsNotNull(app, "<AppObject> not found");
XElement img = app.Elements()
.FirstOrDefault(x => x.Name == XName.Get("Image", "http://tempuri.org/"));
Assert.IsNotNull(img, "<Image> not found");
Assert.AreEqual("StoreApp.png", img.Value);
}
Note that I just mock up and parse string from your XML.
Related
i have a collection of service users, i want to iterate over ServiceUsers and extract value from ServiceUser, (ID, USER_NAME, UN_ID, IP, NAME)
<ServiceUsers xmlns="">
<ServiceUser>
<ID>280334</ID>
<USER_NAME>YVELAMGAMOIYENET12:206322102</USER_NAME>
<UN_ID>731937</UN_ID>
<IP>91.151.136.178</IP>
<NAME>?????????????????????: 123456</NAME>
</ServiceUser>
<ServiceUser>
<ID>266070</ID>
<USER_NAME>ACHIBALANCE:206322102</USER_NAME>
<UN_ID>731937</UN_ID>
<IP>185.139.56.37</IP>
<NAME>123456</NAME>
</ServiceUser>
</ServiceUsers>
my Code looks like this, but i am getting null point exception.
XDocument doc = XDocument.Parse(xml)
List<XElement> xElementList = doc.Element("ServiceUsers").Descendants().ToList();
foreach (XElement element in xElementList)
{
string TEST= element.Element("Name").Value;
comboBoxServiceUser.Items.Add(element.Element("Name").Value);
}
I used the example from XmlSerializer.Deserialize Method as the base for the following snippet that reads the provided xml.
var serializer = new XmlSerializer(typeof(ServiceUsers));
ServiceUsers i;
using (TextReader reader = new StringReader(xml))
{
i = (ServiceUsers)serializer.Deserialize(reader);
}
[XmlRoot(ElementName = "ServiceUsers")]
public class ServiceUsers : List<ServiceUser>
{
}
public class ServiceUser
{
[XmlElement(ElementName = "ID")]
public string Id {get; set;}
}
I think the basis of the problem is your trailing 's' to put it short, You iterate ServiceUser not ServiceUsers
Anyway this runs through fine:
[Fact]
public void CheckIteratorTest()
{
var a = Assembly.GetExecutingAssembly();
string[] resourceNames = a.GetManifestResourceNames();
string nameOf = resourceNames.FirstOrDefault(x => x.Contains("SomeXml"));
Assert.NotNull(nameOf);
using var stream = a.GetManifestResourceStream(nameOf);
Assert.NotNull(stream);
var reader = new StreamReader(stream, Encoding.UTF8);
var serialized = reader.ReadToEnd();
var doc = XDocument.Parse(serialized);
var elemList = doc.Root.Elements("ServiceUser").ToList();
Assert.NotEqual(0, elemList.Count);
foreach(var serviceUser in elemList)
{
System.Diagnostics.Debug.WriteLine($"Name : {serviceUser.Name ?? "n.a."}");
}
}
As being said: XML is case-sensitive. Next issue is .Descendants() returns all the descendant nodes, nested ones, etc, 12 nodes in this case. So NullPointerException will happen even if you fix a "typo".
Here is your fixed code:
XDocument doc = XDocument.Parse(xml);
var xElementList = doc
.Element("ServiceUsers") // picking needed root node from document
.Elements("ServiceUser") // picking exact ServiceUser nodes
.Elements("NAME") // picking actual NAME nodes
.ToList();
foreach (XElement element in xElementList)
{
var TEST = element.Value;
Console.WriteLine(TEST); // do what you were doing instead of console
}
Use doc.Element("ServiceUsers").Elements() to get the<ServiceUser> elements. Then you can loop over the child values of those in a nested loop.
var doc = XDocument.Parse(xml);
foreach (XElement serviceUser in doc.Element("ServiceUsers").Elements()) {
foreach (XElement element in serviceUser.Elements()) {
Console.WriteLine($"{element.Name} = {element.Value}");
}
Console.WriteLine("---");
}
Prints:
ID = 280334
USER_NAME = YVELAMGAMOIYENET12:206322102
UN_ID = 731937
IP = 91.151.136.178
NAME = ?????????????????????: 123456
---
ID = 266070
USER_NAME = ACHIBALANCE:206322102
UN_ID = 731937
IP = 185.139.56.37
NAME = 123456
---
Note: Elements() gets the (immediate) child elements where as Descendants() returns all descendants. Using Elements() gives you a better control and allows you to get the properties grouped by user.
You can also get a specific property like this serviceUser.Element("USER_NAME").Value. Note that the tag names are case sensitive!
I am trying to extract values of : url,ttype,tempTnxId,token,txnStage from the following XML string:
<?xml version="1.0" encoding="UTF-8"?>
<MMP>
<MERCHANT>
<RESPONSE>
<url>https://payment.xyz.com/paynetz/epi/fts</url>
<param name="ttype">abc</param>
<param name="tempTxnId">12319507</param>
<param name="token">x5H9RrhgfXvamaqEl6GpY4uCoXHN%2FlEm%2BUpaaKuMQus%3D</param>
<param name="txnStage">1</param>
</RESPONSE>
</MERCHANT>
</MMP>
So far I have only been able to extract values with index using following code:
foreach (XmlNode node in doc.SelectNodes("/MMP/MERCHANT/RESPONSE/param"))
{
string tempTxnId= doc.SelectNodes("/MMP/MERCHANT/RESPONSE/param")[1].InnerText;//only works with index and not name
}
/MMP/MERCHANT/RESPONSE/param or /MMP/MERCHANT/RESPONSE/ttype does not return anything.
This solution :Getting specified Node values from XML document
does not seem to be working for me.
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlstring);
var result = doc.Elements("table"); ///cant find Elements, Element is is not identified by the compiler
You commented that you cannot select by name ttype.
ttype is a value and not a name.
The element name is param.
The single attribute name of the element param is name.
If you need to get the InnerText of the element param with attribute name equal to ttype (or the other values) then you could do something like:
var xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><MMP><MERCHANT><RESPONSE><url>https://payment.xyz.com/paynetz/epi/fts</url><param name=\"ttype\">abc</param><param name=\"tempTxnId\">12319507</param><param name=\"token\">x5H9RrhgfXvamaqEl6GpY4uCoXHN%2FlEm%2BUpaaKuMQus%3D</param><param name=\"txnStage\">1</param></RESPONSE></MERCHANT></MMP>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
// This gets 4 nodes.
var paramNodes = doc.SelectNodes("/MMP/MERCHANT/RESPONSE/param");
foreach (XmlElement e in paramNodes)
{
Console.WriteLine(e.Attributes[0].Value + "=" + e.InnerText);
}
// These each get a single node.
var ttypeNode = doc.SelectSingleNode("/MMP/MERCHANT/RESPONSE/param[#name=\"ttype\"]");
var tempTxnIdNode = doc.SelectSingleNode("/MMP/MERCHANT/RESPONSE/param[#name=\"tempTxnId\"]");
var tokenNode = doc.SelectSingleNode("/MMP/MERCHANT/RESPONSE/param[#name=\"token\"]");
var txnStageNode = doc.SelectSingleNode("/MMP/MERCHANT/RESPONSE/param[#name=\"txnStage\"]");
Console.WriteLine(ttypeNode.InnerText);
Console.WriteLine(tempTxnIdNode.InnerText);
Console.WriteLine(tokenNode.InnerText);
Console.WriteLine(txnStageNode.InnerText);
You can select node by attribute value like this (assuming this is what you are trying to do):
doc.SelectNodes("/MMP/MERCHANT/RESPONSE/param[#name='ttype']")
.Cast<XmlNode>().ToList()
.ForEach(x=>Console.WriteLine(x.InnerText));
Using xml linq
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);
var response = doc.Descendants("RESPONSE").Select(x => new {
url = (string)x.Element("url"),
ttype = x.Elements().Where(y => (string)y.Attribute("name") == "ttype").Select(z => (string)z).FirstOrDefault(),
tempTxnId = x.Elements().Where(y => (string)y.Attribute("name") == "tempTxnId").Select(z => (string)z).FirstOrDefault(),
token = x.Elements().Where(y => (string)y.Attribute("name") == "token").Select(z => (string)z).FirstOrDefault(),
txnStage = x.Elements().Where(y => (string)y.Attribute("name") == "txnStage").Select(z => (int)z).FirstOrDefault()
}).FirstOrDefault();
}
}
}
I'm having a following XML, in that I'm having two Nodes namely "param" but the Names are different. I need to get the value by Name from the node param. Kindly look at the code
void Main()
{
string _commentXml = string.Format("<root>{0}{1}</root>"
, "<param name=\"Super\">Parameter One</param>"
, "<param name=\"Supreme\">Parameter Two</param>");
XmlDocument _comment = new XmlDocument();
_comment.LoadXml(_commentXml);
XElement element = XElement.Load(_comment.DocumentElement.CreateNavigator().ReadSubtree());
TryGetElementValue(element, "param").Dump();
}
public string TryGetElementValue(XElement parentEl, string elementName, string defaultValue = null)
{
var foundEl = parentEl.Element(elementName);
if (foundEl != null)
{
var xyz = foundEl.Elements("param");
if (xyz != null)
{
return xyz.First(x => x.Attribute("name").Value == "Super").Value;
}
}
return defaultValue;
}
I can't able to get the value of param with name=Super
I refereed one of the stack-overflow question which is opt for this requirement but I can't.
Referred: XDocument get XML element by the value of its name attribute
Kindly assist me.
Why all this mess?
XDocument has a Descendants method and with linq it's easy:
var xdoc = XDocument.Parse(_commentXml);
var xel = xdoc.Descendants("param")
.Where(xElement => xElement.Attribute("name")?.Value == "Super");
You can also user XPath.
var element = doc.XPathSelectElement("/path/to/element/I/want");
In your case it would be something like this:
var element = doc.XPathSelectElement("/root/param[#name="Super"]");
Check here for more info:
https://stackoverflow.com/a/11224645/1582065
I have to process XML response from a 3rd party REST API over which I do not have control. problem is the XML response in not in proper format something like this:
<XML>
<Meta>
<Status>Success</Status>
<Debug/>
</Meta>
<Result>
<abc>
<DivisionID>tttttttttt</DivisionID>
<UserName><![CDATA[ xxx#xxxxx]]></UserName>
<UserFirstName>xxxx</UserFirstName>
<UserLastName>xxxx</UserLastName>
<UserAccountType>xxxxxxx</UserAccountType>
<UserEmail>xxxxx#xxxxx.xom</UserEmail>
<UserAccountStatus>Active</UserAccountStatus>
</abc>
<def>
<DivisionID/>
<UserName><![CDATA[ xxx#xxxx]]></UserName>
<UserFirstName>yyyy</UserFirstName>
<UserLastName>vvvvvv</UserLastName>
<UserAccountType>uuuuuuuuu</UserAccountType>
<UserEmail>oooo#vvvvvv</UserEmail>
<UserAccountStatus>Active</UserAccountStatus>
</def>
....contd
</Result>
var requestUri = new Uri(uriString);
HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(requestUri);
var httpresponse = (HttpWebResponse)httprequest.GetResponse();
Person people = new Person();
List<Person> lstPerson = (from _person in xmlDoc.Document.Element("Result").Elements("Result")
select new Person
{
userName = Xdocument.Load(httpresponse.GetResponseStream()).Root.ToString(),
userEmail = _person.Element("UserEmail").Value
}).ToList();
I need to retrieve the node with value "abc" and "def" and store them in UserName which itself is root node, and also to retrieve the values in between them. so how to do this I tried various ways but was unable to to so.
Update
To create a list of your Person classes using element names for userName and the value of the UserEmail sub-element for userEmail, you can do the following:
try
{
// Load the XML from the external site
XDocument xmlDoc;
var requestUri = new Uri(uriString);
HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(requestUri);
using (var httpresponse = (HttpWebResponse)httprequest.GetResponse())
using (var stream = httpresponse.GetResponseStream())
using (var reader = new StreamReader(stream))
{
xmlDoc = XDocument.Load(reader);
}
// Extract the name & email.
var people = xmlDoc
// Get the "Result" node
.Root.Elements(xmlDoc.Root.Name.Namespace + "Result")
// Loop through its elements
.SelectMany(result => result.Elements())
// Deserialize the element name and UserEmail sub-element value as a Person
.Select(element => new Person { userName = element.Name.LocalName, userEmail = element.Element(xmlDoc.Root.Name.Namespace + "UserEmail").ValueSafe() })
.ToList();
// Process or return the list of people
}
catch (Exception ex)
{
// Handle any web exception encountered.
Debug.WriteLine(ex);
// Or rethrow if it can't be handled here
throw;
}
Using the extension method
public static class XObjectExtensions
{
public static string ValueSafe(this XElement element)
{
return element == null ? null : element.Value;
}
}
Original Answer
Your question is unclear. But if you are asking
If I have loaded an XDocument from some XML, is there a way using XmlSerializer to deserialize a portion which is an embedded list when each element of the list has a custom element name?
Then you can do this as follows:
Loading the XDocument
Navigate to the list you want to deserialize.
For each element, remember the element name, then overwrite it with the class name.
Use XElement.CreateReader() to create an XmlReader for just that element, then pass it to an XmlSerializer for deserialization.
I.e.:
// Load the document
var doc = XDocument.Parse(xml);
var people = doc
// Navigate to the list
.Root.Elements("Result")
.SelectMany(r => r.Elements())
// Deserialize each element in the list as a KeyValuePair<string, Person>
.Select(element =>
{
var name = element.Name;
element.Name = typeof(Person).DefaultXmlElementName(); // Overwrite name with name used by serializer.
using (var reader = element.CreateReader())
{
var person = (Person)new XmlSerializer(typeof(Person)).Deserialize(reader);
return new KeyValuePair<string, Person>(name.LocalName, person);
}
})
.ToList();
Given the XML string
string xml = #"<XML>
<Meta>
<Status>Success</Status>
<Debug/>
</Meta>
<Result>
<abc>
<DivisionID>tttttttttt</DivisionID>
<UserName><![CDATA[ xxx#xxxxx]]></UserName>
<UserFirstName>xxxx</UserFirstName>
<UserLastName>xxxx</UserLastName>
<UserAccountType>xxxxxxx</UserAccountType>
<UserEmail>xxxxx#xxxxx.xom</UserEmail>
<UserAccountStatus>Active</UserAccountStatus>
</abc>
<def>
<DivisionID/>
<UserName><![CDATA[ xxx#xxxx]]></UserName>
<UserFirstName>yyyy</UserFirstName>
<UserLastName>vvvvvv</UserLastName>
<UserAccountType>uuuuuuuuu</UserAccountType>
<UserEmail>oooo#vvvvvv</UserEmail>
<UserAccountStatus>Active</UserAccountStatus>
</def>
</Result>
</XML>
";
Using the extension method:
public static class XmlSerializationHelper
{
public static string DefaultXmlElementName(this Type type)
{
var xmlType = type.GetCustomAttribute<XmlTypeAttribute>();
if (xmlType != null && !string.IsNullOrEmpty(xmlType.TypeName))
return xmlType.TypeName;
var xmlRoot = type.GetCustomAttribute<XmlRootAttribute>();
if (xmlRoot != null && !string.IsNullOrEmpty(xmlRoot.ElementName))
return xmlRoot.ElementName;
return type.Name;
}
}
There is a similar question, but it seems that the solution didn't work out in my case: Weirdness with XDocument, XPath and namespaces
Here is the XML I am working with:
<?xml version="1.0" encoding="utf-8"?>
<Report Id="ID1" Type="Demo Report" Created="2011-01-01T01:01:01+11:00" Culture="en" xmlns="http://demo.com/2011/demo-schema">
<ReportInfo>
<Name>Demo Report</Name>
<CreatedBy>Unit Test</CreatedBy>
</ReportInfo>
</Report>
And below is the code that I thought it should be working but it didn't...
XDocument xdoc = XDocument.Load(#"C:\SampleXML.xml");
XmlNamespaceManager xnm = new XmlNamespaceManager(new NameTable());
xnm.AddNamespace(String.Empty, "http://demo.com/2011/demo-schema");
Console.WriteLine(xdoc.XPathSelectElement("/Report/ReportInfo/Name", xnm) == null);
Does anyone have any ideas?
Thanks.
If you have XDocument it is easier to use LINQ-to-XML:
var document = XDocument.Load(fileName);
var name = document.Descendants(XName.Get("Name", #"http://demo.com/2011/demo-schema")).First().Value;
If you are sure that XPath is the only solution you need:
using System.Xml.XPath;
var document = XDocument.Load(fileName);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("empty", "http://demo.com/2011/demo-schema");
var name = document.XPathSelectElement("/empty:Report/empty:ReportInfo/empty:Name", namespaceManager).Value;
XPath 1.0, which is what MS implements, does not have the idea of a default namespace. So try this:
XDocument xdoc = XDocument.Load(#"C:\SampleXML.xml");
XmlNamespaceManager xnm = new XmlNamespaceManager(new NameTable());
xnm.AddNamespace("x", "http://demo.com/2011/demo-schema");
Console.WriteLine(xdoc.XPathSelectElement("/x:Report/x:ReportInfo/x:Name", xnm) == null);
you can use the example from Microsoft - for you without namespace:
using System.Xml.Linq;
using System.Xml.XPath;
var e = xdoc.XPathSelectElement("./Report/ReportInfo/Name");
should do it
To work w/o default namespace suffix, I automatically expand the path.
Usage: SelectElement(xdoc.Root, "/Report/ReportInfo/Name");
private static XElement SelectElement(XElement startElement, string xpathExpression, XmlNamespaceManager namespaceManager = null) {
// XPath 1.0 does not have support for default namespace, so we have to expand our path.
if (namespaceManager == null) {
var reader = startElement.CreateReader();
namespaceManager = new XmlNamespaceManager(reader.NameTable);
}
var defaultNamespace = startElement.GetDefaultNamespace();
var defaultPrefix = namespaceManager.LookupPrefix(defaultNamespace.NamespaceName);
if (string.IsNullOrEmpty(defaultPrefix)) {
defaultPrefix = "ᆞ";
namespaceManager.AddNamespace(defaultPrefix, defaultNamespace.NamespaceName);
}
xpathExpression = AddPrefix(xpathExpression, defaultPrefix);
var selected = startElement.XPathSelectElement(xpathExpression, namespaceManager);
return selected;
}
private static string AddPrefix(string xpathExpression, string prefix) {
// Implementation notes:
// * not perfect, but it works for our use case.
// * supports: "Name~~" "~~/Name~~" "~~#Name~~" "~~[Name~~" "~~[#Name~~"
// * does not work in complex expressions like //*[local-name()="HelloWorldResult" and namespace-uri()='http://tempuri.org/']/text()
// * does not exclude strings like 'string' or function like func()
var s = Regex.Replace(xpathExpression, #"(?<a>/|\[#|#|\[|^)(?<name>\w(\w|[-])*)", "${a}${prefix}:${name}".Replace("${prefix}", prefix));
return s;
}
If anyone has a better solution to find element and attribute names, feel free to change this post.