Reading all root node names in an XML document - c#

I'm sure all the pros out there would find this very trivial but I need a quick solution for this in C#
I'm retrieving an xml schema of a view in share point which looks like this:
<FieldRef Name="LinkTitle"/><FieldRef Name="Author0"/><FieldRef Name="ID"/>
I want to parse this and only retrieve the Name's of each root element in this schema.
currently this is the code I'm working on , need some help with it
String fieldvals = view.ViewFields.SchemaXml.ToString();
XmlDocument reader = new XmlDocument(); ;
reader.LoadXml(fieldvals);
String xpath = "/";
var nodes = reader.SelectNodes(xpath);
foreach (XmlNode childrenNode in nodes)
{
Console.WriteLine(childrenNode.SelectSingleNode("//field1").Value);
}
Apparently, when this piece of code executes, I get an exception saying that more than one root node is present which is true of course .. but I'm not able to figure out the correct code to access every root node and extract it's name!

Wrap your xml fragment within a root node and then you can use linq to xml to retrieve a string array of those names like this:
var xml = XElement.Parse(xmlString);
var names=xml.Elements().Attributes(#"Name").Select(attrib => attrib.Value);

You should wrap your xml in some root node as an XML can have only one Root Node as below :
<FieldRefs>
<FieldRef Name="LinkTitle"/>
<FieldRef Name="Author0"/>
<FieldRef Name="ID"/>
</FieldRefs>
And then your code will execute fine.
String fieldvals = view.ViewFields.SchemaXml.ToString();
XmlDocument reader = new XmlDocument(); ;
reader.LoadXml(fieldvals);
String xpath = "/FieldRefs/FieldRef";
var nodes = reader.SelectNodes(xpath);
foreach (XmlNode childrenNode in nodes)
{
/*Process here*/
}

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.

Reading XML nodes causing issues

I have one XML string, I am trying to read that using C#, but I am not getting child nodes. I am getting entire XML as inner XML string. I am not able to read the nodes. Here is my XML string and my code.
<Filters FilterName="706337_test">
<MemberName>Dorvil</MemberName>
<MemberId />
<ProviderName />
<ProviderId>706337</ProviderId>
<SelectedProjects>5030003</SelectedProjects>
<CNAChartSelected>false</CNAChartSelected>
<OldProject>false</OldProject>
</Filters>
C# code trying to read the XML nodes
XmlDocument xml = new XmlDocument();
xml.LoadXml(xmlstring);
XmlNodeList xnList = xml.SelectNodes("/Filters");
I can see only one child node that too filters, I need to read MemberId, MemberName etc., how to read them?
This is because your string in SelectNodes is wrong:
var xml = new XmlDocument();
xml.LoadXml(xmlstring);
var xnList1 = xml.SelectNodes("/Filters"); //list of 1 element
var xnList2 = xml.SelectNodes("/Filters/*"); //list of 7 elements
foreach (XmlNode node in xnList2)
{
Console.WriteLine(node.OuterXml);
}
Also you can use this:
var xElements = XElement.Parse(xmlstring).Elements();
foreach (var element in xElements)
{
Console.WriteLine(element);
}
You need to tell the app which nodes to read..
XmlDocument xml = new XmlDocument();
xml.LoadXml(xmlstring);
XmlNodeList xnList = xml.SelectNodes("/Filters");
foreach (XmlNode node in xnList)
{
string memberName = node["MemberName"].InnerText;
}
This lets the app know to read what is inside the MemberName node.. Do the same for the other nodes and post back your results. Debug as you go to see what you are getting out of each node.

Xdocument, get each element(s) value

I have xml as follows:
<Reports>
<report>
<name>By Book</name>
<report_type>book</report_type>
<Object>Count Change</Object>
<Slicers detail="detail">
<Namespace>EOD</Namespace>
<BookNode>HighLevel</BookNode>
<DateFrom>T-2</DateFrom>
<DateTo>T-1</DateTo>
<System>NewSystem</System>
</Slicers>
</report>
</Reports>
I simply want to loop through the value of each element of the Xdocument (pref would be any element under Slicers) but to start with just all elements.
When I run the following:
var slicers = from c in config.Elements("Reports")
select c.Value ;
foreach (var xe in slicers)
{
Console.WriteLine(xe);
}
The output is a single line concatenating all the values together.
"By BookbookCount ChangeEODHighLevelT-2T-1NewSystem"
I want to loop through them one at a time, 'By Book' first, run some code then book etc etc.
I am sure this is simple, but cant get round it. I have tried foreach(Xelement in query) but same resulst
i would do it something like this;
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
//load in your xml here
XmlNodeList xnList = doc.SelectNodes("nodeYou'reLookingFor");
//for getting just the splicers you could do "Reports/report/Slicers"
foreach (XmlNode node in xnList)
string namespace = node["Namespace"].InnerText;
//go through all your nodes here
you're creating a xmldoc, loading your xml into it, creating a list which holds each node in the list (at a specified Xpath), and then looping through each. in the loop you can do whatever you want by referencing
node["nodenamehere"].InnerText

How can I read specific elements from XML string using XMLREADER in C#

I have XML String:
<GroupBy Collapse=\"TRUE\" GroupLimit=\"30\">
<FieldRef Name=\"Department\" />
</GroupBy>
<OrderBy>
<FieldRef Name=\"Width\" />
</OrderBy>
I am new in C#. I tried to read the Name attribute of the FieldRef element for both elements but I could not. I used XMLElement , is there any way to pick these two values?
Despite the posting of invalid XML (no root node), an easy way to iterate through the <FieldRef> elements is to use the XmlReader.ReadToFollowing method:
//Keep reading until there are no more FieldRef elements
while (reader.ReadToFollowing("FieldRef"))
{
//Extract the value of the Name attribute
string value = reader.GetAttribute("Name");
}
Of course a more flexible and fluent interface is provided by LINQ to XML, perhaps it would be easier to use that if available within the .NET framework you are targeting? The code then becomes:
using System.Xml.Linq;
//Reference to your document
XDocument doc = {document};
/*The collection will contain the attribute values (will only work if the elements
are descendants and are not direct children of the root element*/
IEnumerable<string> names = doc.Root.Descendants("FieldRef").Select(e => e.Attribute("Name").Value);
try this:
string xml = "<GroupBy Collapse=\"TRUE\" GroupLimit=\"30\"><FieldRef Name=\"Department\" /></GroupBy><OrderBy> <FieldRef Name=\"Width\" /></OrderBy>";
xml = "<root>" + xml + "</root>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
foreach (XmlNode node in doc.GetElementsByTagName("FieldRef"))
Console.WriteLine(node.Attributes["Name"].Value);

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>

Categories