Traverse the XML using Attribute Value - c#

I have to take all the child nodes of the following node.
<Contact Enabled="true" Hash="1105403056" Id="-1" Index="0" UserTypeName="Name" ViewClassName="employeeContact">
I'm using the following code but the result is always null
var doc = new XmlDocument();
doc.Load("alarm1.xml");
var tabNodes = doc.SelectNodes("//Contact [#UserTypeName=Name]");
The same thing works if I put this:
var doc = new XmlDocument();
doc.Load("alarm1.xml");
var tabNodes = doc.SelectNodes("//Contact [#Hash=1105403056]");
Can someone help me here please? Tell me what is wrong in the first method?

Related

Parsing xml response - Select specific node based on value of another node

I'm new to xml so I'm not sure if I worded the question correctly, but I will do my best to explain.
Basically, I'm trying to parse an xml response in C# such as the one below:
<Premium>
<TotalPremiumAmount>87</TotalPremiumAmount>
<StandardPremium>87</StandardPremium>
<OptionalPremium>0</OptionalPremium>
<StandardTax>0</StandardTax>
<OptionalTax>0</OptionalTax>
<ExtendedTax>0</ExtendedTax>
<ExtendedPremium>0</ExtendedPremium>
<PromotionalPremium>0</PromotionalPremium>
<FeesPremium>0</FeesPremium>
<FeesTax>0</FeesTax>
<StandardFeesPremium>0</StandardFeesPremium>
<OptionalFeesPremium>0</OptionalFeesPremium>
<Tax>0</Tax>
<StandardPremiumDistribution>
<Travelers>
<Traveler>
<TravelerPremium>42</TravelerPremium>
<TravelerTax>0</TravelerTax>
</Traveler>
<Traveler>
<TravelerPremium>45</TravelerPremium>
<TravelerTax>0</TravelerTax>
</Traveler>
</Travelers>
</StandardPremiumDistribution>
<PackagePremiumDistribution>
<Packages>
<Package>
<PackageID>20265</PackageID>
<PackageName />
<PackageTypeID>12</PackageTypeID>
<Premium>87</Premium>
<Fees>0</Fees>
<Tax>0</Tax>
<Travelers>
<Traveler>
<TravelerID>0</TravelerID>
<Premium>42</Premium>
<Tax>0</Tax>
</Traveler>
<Traveler>
<TravelerID>1</TravelerID>
<Premium>45</Premium>
<Tax>0</Tax>
</Traveler>
</Travelers>
</Package>
</Packages>
</PackagePremiumDistribution>
</Premium>
I would like to get the value of the (Traveler) Premium. In the case of only one traveler, I have been using an XMLDocument and the 'SelectSingleNode" function. For example I could do something like:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlResponse);
var premium = xmlDoc.SelectSingleNode("//TravelerPremium").InnerText;
But this wouldn't work when multiple travelers are returned under one plan. For example, I need the premium when TravelerID = 0. How would I go about doing this?
Thanks.
Using XmlDocument:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlResponse);
var premium = xmlDoc.SelectSingleNode("//Premium[../TravelerID = '0']")
You could also iterate through the nodes if multiple could match on this condition like so:
foreach(var premium in xmldoc.SelectNodes("//Premium[../TravelerID = '0']")
{
// do work on each premium node where TravelerID = 0
}
I'd encourage you to look into using LINQ to XML - it's generally easier to work with and will be more performant in most cases. You could even still use XPath expressions, but the following would work:
XDocument xdoc = XDocument.Load(xmlResponse);
var premium = (string)xdoc.Descendants("Traveler").Where(x => (string)x.Element("TravelerID") == "0").Element("Premium");
Assuming your xml looks like that, try something like this:
XmlDocument doc = new XmlDocument();
xmlDoc.Load(xmlResponse);
if (doc.HasChildNodes)
{
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
if (node.Name == "StandardPremiumDistribution")
{
XmlNodeList xnList = node.SelectNodes("//Travelers");
double travelerPremium= xnList.Item(z).FirstChild.InnerText);
}}
Based on this, I think you're gonna do it.
Let's suppose you have a file called XMLFile1.xml with the XML you posted you can iterate through all your TravelerPremium with the following code:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("XMLFile1.xml");
XmlNodeList premiums = xmlDoc.SelectNodes("//TravelerPremium");
foreach(XmlNode node in premiums)
{
MessageBox.Show(node.FirstChild.InnerText);
}
You can also acces the other elements with similar code.

Insert node in xml document using c#

I was trying to insert an XML node in XML document at a specific position.
This is my xml:
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<readContract xmlns="http://implementation.company.schema.reference">
<ContactNumbers>10158</ContactNumbers>
<productGroups>0085</productGroups>
<indicationBalanceInfo>false</indicationBalanceInfo>
<indicationBlocked>true</indicationBlocked>
</readContract>
</Body>
</Envelope>
And am trying to insert another tag <productGroups>0093</productGroups> below to the tag <productGroups>0085</productGroups>
Expecting like the below:
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<readContract xmlns="http://implementation.company.schema.reference">
<ContactNumbers>10158</ContactNumbers>
<productGroups>0085</productGroups>
<productGroups>0093</productGroups>
<indicationBalanceInfo>false</indicationBalanceInfo>
<indicationBlocked>true</indicationBlocked>
</readContract>
</Body>
</Envelope>
Used the below C# code to achieve it.
XmlDocument doc = new XmlDocument();
string inputxml = this.StServiceCallActivity5.InputEnvelope.InnerXml.ToString();
//Here inputxml contains whole xml document.
string addxml = "<productGroups>0093</productGroups>";
doc.LoadXml(inputxml);
XmlDocumentFragment xmlDocFrag = doc.CreateDocumentFragment();
xmlDocFrag.InnerXml = addxml;
XmlElement parentEle = doc.DocumentElement;
parentEle.AppendChild(xmlDocFrag);
And it returns value like
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<readContract xmlns="http://implementation.company.schema.reference">
<ContactNumbers>10158</ContactNumbers>
<productGroups>0085</productGroups>
<productGroups>0093</productGroups>
<indicationBalanceInfo>false</indicationBalanceInfo>
<indicationBlocked>true</indicationBlocked>
</readContract>
</Body>
<productGroups xmlns="">0093</productGroups>
</Envelope>
Am a newbie to C# code, kindly help me to get the XML doc as expected.
your help is much appreciated.
When you do this:
XmlElement parentEle = doc.DocumentElement;
parentEle.AppendChild(xmlDocFrag);
You're appending the node to the root of the document. You probably wanted to select the actual readContract node that the item is supposed to be appended into. As an example:
XmlNode newNode = doc.CreateNode(XmlNodeType.Element, "productGroup", "");
newNode.InnerText = "something";
XmlNode readContractNode = doc["Envelope"]["Body"]["readContract"];
XmlElement groups = readContractNode["productGroups"];
readContractNode.InsertAfter(newNode, groups);
Of course you'd probably want to handle the case where there are already multiple child productGroup elements, but the idea is the same.
Looks like namespaces are causing the problem. This worked for me:
XmlDocument doc = new XmlDocument();
doc.LoadXml(File.ReadAllText("XMLFile1.xml"));
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("ns1", "http://schemas.xmlsoap.org/soap/envelope/");
ns.AddNamespace("ns2", "http://implementation.company.schema.reference");
var rootNode = doc.SelectSingleNode("//ns1:Envelope", ns);
var readContractNode = rootNode.FirstChild.FirstChild;
var newNode = doc.CreateNode(XmlNodeType.Element, "productGroups", "http://implementation.company.schema.reference");
newNode.InnerText = "0093";
readContractNode.InsertAfter(newNode, readContractNode.SelectSingleNode("//ns2:productGroups", ns));
Or if you don't fancy namespaces like I me, you can try a bit more "brute-forcy" approach:
XmlDocument doc = new XmlDocument();
doc.LoadXml(File.ReadAllText("XMLFile1.xml"));
var newNode = doc.CreateNode(XmlNodeType.Element, "productGroups", "http://implementation.company.schema.reference");
newNode.InnerText = "0093";
doc.FirstChild.FirstChild.FirstChild.InsertAfter(newNode, doc.FirstChild.FirstChild.FirstChild.FirstChild.NextSibling);
It can be optimized but I think it helps to make the point that root cause is the different namespaces in the document.
You may want to use XmlNode.InsertAfter Method.
public virtual XmlNode InsertAfter(
XmlNode newChild,
XmlNode refChild
)
Where
newChild = The XmlNode to insert
and
refChild = The XmlNode that is the reference node. The newNode is placed after the refNode
Please check this link for information.
And check this link with answer on SO.
P.S.
Always check other answers before posting new question.

Linq-to-Xml prefix parse error

I am currently trying to learn how to parse data, and its a bit confusing. can someone check out my code and see what I'm doing wrong or if im even heading in the right direction.
XML File:
<xml xmlns:a='BLAH'
xmlns:b='BLAH'
xmlns:c='BLAH'
xmlns:d='BLAH'>
<a:info>
<b:cat Option1='blah' Option2='blah' Option3='blah' />
</a:info>
</xml>
C# Code:
XmlDocument doc = new XmlDocument();
doc.Load(richTextBox2.Text);
XmlNamespaceManager man = new XmlNamespaceManager(doc.NameTable);
man.AddNamespace("a", "BLAH");
man.AddNamespace("b", "BLAH");
man.AddNamespace("c", "BLAH");
man.AddNamespace("d", "BLAH");
XmlNode temps = doc.SelectSingleNode("/a:info/b:cat/Option1/", man);
richTextBox1.Text = temps.InnerText;
I am new to C#, I cant find a good example explaining how to successfully use loops to find more then one:
<b:chat />
You are using the wrong API if you're looking for LINQ to XML. Use the XDocument class instead.
Assume the following input XML-document (please note namespace URLs):
<xml xmlns:a='http://localhost/scheme_a'
xmlns:b='http://localhost/scheme_b'
xmlns:c='http://localhost/scheme_c'
xmlns:d='http://localhost/scheme_d'>
<a:info>
<b:cat Option1='1' Option2='1' Option3='1' />
</a:info>
<a:info>
<b:cat Option1='2' Option2='2' Option3='2' />
</a:info>
</xml>
There are the ways to get all <b:chat /> elements.
XmlDocument class:
var xmlDocument = new XmlDocument();
xmlDocument.Load(...);
var xmlNamespaceManager = new XmlNamespaceManager(xmlDocument.NameTable);
xmlNamespaceManager.AddNamespace("a", "http://localhost/scheme_a");
xmlNamespaceManager.AddNamespace("b", "http://localhost/scheme_b");
xmlNamespaceManager.AddNamespace("c", "http://localhost/scheme_c");
xmlNamespaceManager.AddNamespace("d", "http://localhost/scheme_d");
var bCatNodes = xmlDocument.SelectNodes("/xml/a:info/b:cat", xmlNamespaceManager);
var option1Attributes = bCatNodes.Cast<XmlNode>().Select(node => node.Attributes["Option1"]);
// Also, all Option1 attributes can be retrieved directly using XPath:
// var option1Attributes = xmlDocument.SelectNodes("/xml/a:info/b:cat/#Option1", xmlNamespaceManager).Cast<XmlAttribute>();
LINQ to XML XDocument Class. XName can be passed with a namespace to Descendants() and Element() methods.
Use Descendants() to get all <b:chat /> elements.
var xDocument = XDocument.Load(...);
XNamespace xNamespace = "http://localhost/scheme_b";
var xElements = xDocument.Descendants(xNamespace + "cat");
// For example, get all the values of Option1 attribute for the b:chat elements:
var options1 = xElements.Select(element => element.Attribute("Option1")).ToList();

Append Multiple Similar XML Nodes to XML Document

I'm currently working with an XML request, and am trying to create a Reply Document that has multiple child nodes of the same name in the call, so what I'm trying to return is:
<Reply Document>
<ConfirmationItem name = "One">
<ItemDetail />
</ConfirmationItem>
<ConfirmationItem name = "Two">
<ItemDetail />
</ConfirmationItem>
...
<ConfirmationItem name = "Twenty">
<ItemDetail />
</ConfirmationItem>
</Reply Document>
I did a bit of research and found this thread: XmlReader AppendChild is not appending same child value in which the accepted answer was that the OP had to create new Elements to be able to append to the end instead of overwrite the first.
My original code is below, it creates the XmlNode from the incoming Request and appends the result to the XmlDocument itself:
//p_transdoc is the XmlDocument that holds all the items to process.
XmlNodeList nodelst_cnfrm = p_transdoc.SelectNodes("//OrderRequest");
foreach (XmlNode node in nodelst_cnfrm)
{
//this is just an XML Object
XmlNode node_cnfrm_itm = this.CreateElement("ConfirmationItem");
node_cnfrm_itm.Attributes.Append(this.CreateAttribute("name")).InnerText = p_transdoc.Attributes["name"].InnerText;
XmlNode node_itmdtl = this.CreateElement("ItemDetail");
node_cnfrm_itm.AppendChild(node_itmdtl);
//xml_doc is the return XML request
xml_doc.AppendChild(node_cnfrm_itm);
}
So after reading that thread and the answer, I tried to change the code to use a new XmlElement each pass through.
//p_transdoc is the XmlDocument that holds all the items to process.
XmlNodeList nodelst_cnfrm = p_transdoc.SelectNodes("//OrderRequest");
foreach (XmlNode node in nodelst_cnfrm)
{
XmlElement node_cnfrm_itm = new XmlElement();
node_cnfrm_itm = this.CreateElement("ConfirmationItem");
node_cnfrm_itm.Attributes.Append(this.CreateAttribute("name")).InnerText = p_transdoc.Attributes["name"].InnerText;
XmlElement node_itmdtl = new XmlElement();
node_itmdtl = this.CreateElement("ItemDetail");
node_cnfrm_itm.AppendChild(node_itmdtl);
//xml_doc is the return XML request
xml_doc.AppendChild(node_cnfrm_itm);
}
But not only does that not work, it returns a server error. So I've come to you for help. Right now this code is just returning one ConfirmationItem. How would I be able to append the ConfirmationItem to the end of the Document instead of overwrite it, to be able to return as many as were sent in?
(I should point out that this code has been heavily formatted for ease of readability, simplicity, and to reduce clutter. Any typographical errors are purely because of the Asker's internal failure at effective proofreading).
Assuming xml_doc is the xml with the ConfirmationItems you need to create the XmlElements with the new XmlDocument. XmlDocument.CreateElement. Hence I use the Linq extension method OfType<>() here to only return the XmlNode objects of the type XmlElement.
// dummy data
XmlDocument p_transdoc = new XmlDocument();
p_transdoc.LoadXml(#"
<root name='rootAttribute'>
<OrderRequest name='one' />
<OrderRequest name='two' />
<OrderRequest name='three' />
</root>
");
XmlDocument xml_doc = new XmlDocument();
xml_doc.LoadXml("<ReplyDocument />");
foreach (var node in p_transdoc.SelectNodes("//OrderRequest").OfType<XmlElement>())
{
XmlElement node_cnfrm_itm = xml_doc.CreateElement("ConfirmationItem");
node_cnfrm_itm = xml_doc.DocumentElement.AppendChild(node_cnfrm_itm) as XmlElement;
node_cnfrm_itm.SetAttribute("name", node.GetAttribute("name"));
XmlElement node_itmdtl = xml_doc.CreateElement("ItemDetail");
node_itmdtl = node_cnfrm_itm.AppendChild(node_itmdtl) as XmlElement;
}
The method CreateElement returns an XmlElement so you can use the methods SetAttribute and GetAttribute.
The code: p_transdoc.Attributes["name"].InnerText doesn't seem right. If you want to get the attributes for the root element of the document you need to type: p_transdoc.DocumentElement.GetAttribute("name")
IMO this is MUCH easier if you're using Linq to XML.
In Linq to XML this would be similar to (some variables have different names):
// dummy data
var transDoc = XDocument.Parse(#"
<root name='rootAttribute'>
<OrderRequest name='one' />
<OrderRequest name='two' />
<OrderRequest name='three' />
</root>");
var xmlDoc = XDocument.Parse("<ReplyDocument />");
xmlDoc.Root.Add(
transDoc.Root.Elements("OrderRequest").Select(o =>
new XElement("ConfirmationElement",
new XAttribute("name", (string)o.Attribute("name")),
new XElement("ItemDetail"))));
Both examples output:
<ReplyDocument>
<ConfirmationElement name="one">
<ItemDetail />
</ConfirmationElement>
<ConfirmationElement name="two">
<ItemDetail />
</ConfirmationElement>
<ConfirmationElement name="three">
<ItemDetail />
</ConfirmationElement>
</ReplyDocument>

Read attribute from xml

Can someone help me read attribute ows_AZPersonnummer with asp.net using c# from this xml structure
<listitems
xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
xmlns:rs="urn:schemas-microsoft-com:rowset"
xmlns:z="#RowsetSchema"
xmlns="http://schemas.microsoft.com/sharepoint/soap/">
<rs:data ItemCount="1">
<z:row
ows_AZNamnUppdragsansvarig="Peter"
ows_AZTypAvUtbetalning="Arvode till privatperson"
ows_AZPersonnummer="196202081276"
ows_AZPlusgiro="5456436534"
ows_MetaInfo="1;#"
ows__ModerationStatus="0"
ows__Level="1" ows_ID="1"
ows_owshiddenversion="6"
ows_UniqueId="1;#{11E4AD4C-7931-46D8-80BB-7E482C605990}"
ows_FSObjType="1;#0"
ows_Created="2009-04-15T08:29:32Z"
ows_FileRef="1;#uppdragsavtal/Lists/Uppdragsavtal/1_.000"
/>
</rs:data>
</listitems>
And get value 196202081276.
Open this up in an XmlDocument object, then use the SelectNode function with the following XPath:
//*[local-name() = 'row']/#ows_AZPersonnummer
Basically, this looks for every element named "row", regardless of depth and namespace, and returns the ows_AZPersonnummer attribute of it. Should help avoid any namespace issues you might be having.
The XmlNamespaceManager is your friend:
string xml = "..."; //your xml here
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNamespaceManager nsm = new XmlNamespaceManager(new NameTable());
nsm.AddNamespace("z", "#RowsetSchema");
XmlNode n = doc.DocumentElement
.SelectSingleNode("//#ows_AZPersonnummer", nsm);
Console.WriteLine(n.Value);
You can also use LINQ to XML:
XDocument xd = XDocument.Parse(xml);
XNamespace xns = "#RowsetSchema";
string result1 = xd.Descendants(xns + "row")
.First()
.Attribute("ows_AZPersonnummer")
.Value;
// Or...
string result2 =
(from el in xd.Descendants(xns + "row")
select el).First().Attribute("ows_AZPersonnummer").Value;
I'd say you need an XML parser, which I believe are common. This looks like a simple XML structure, so the handling code shouldn't be too hard.
Use <%# Eval("path to attribute") %> but you need to load the xml has a DataSource.
Otherwise you can load it using XmlTextReader. Here's an example.

Categories