Getting an element tree broken down from an XML - c#

I have an XML and I am try to break down the elements so that it shows the path of every element. I have put an example of the code below and what the output I want to see.
The XML data is as per the below.
<a:Clients>
<a:Client1>
<a:Initial>
<a:FirstName>data</a:FirstName>
<a:LastName>data</a:LastName>
</a:Initial>
<a:FirstName>data</a:FirstName>
<a:MiddleName />
<a:LastName>data</a:LastName>
<a:Title>Mr</a:Title>
<a:MaritalStatus>Divorced</a:MaritalStatus>
<a:MaidenName>data</a:MaidenName>
I want to output it so it shows as
Clients/Client1
Clients/Client1/Initilal
Clients/Client1/Initial/FirstName
etc So I want to get all the elements of my xml to appear in this order for a large XML document. I dont want any data that sits inbetween the tags, Just the element paths.
I have written the below code in vs 2015 - Console Application to try and output it in this order but it is not working correctly. The c# code im using is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
XNamespace ns = "http://www.test.net/author/1.0";
XDocument doc = XDocument.Load("test.xml");
var allElements = doc.Descendants();
foreach (var element in allElements)
Console.WriteLine(allElements);
Console.ReadLine();
}
}
}
The output im currently getting is:
Output
I have looked over Stack Overflow for answers but nothing seems to be exactly what I need and the ones I have tried have not worked either. Not sure if there is an easier way to do this?
Help would be greatly appreciated

I managed to do it in reversed order:
Client1/Clients
So i just entered all into a list and print it in reversed order later to be in the right order.
XDocument doc = XDocument.Load("test.xml");
var allElements = doc.Descendants();
var elemants = doc.Elements();
foreach (var element in allElements)
{
var current = element;
List<string> names = new List<string>();
while (current.Parent != null)
{
names.Add(current.Name.LocalName);
current = current.Parent;
}
names.Add(current.Name.LocalName);
names.Reverse();
foreach(var name in names)
Console.Write(name + "/");
Console.WriteLine();
}
I hope thats what you need.

Related

How to use XmlDocument.SelectSingleNode() with a default namespace?

I'm trying to pull some values from some customer-supplied XML documents, using XmlDocument.SelectSingleNode().
SelectSingleNode() takes an XPATH string, and very simple searches are failing. E.g. with this:
<?xml version="1.0"?>
<xmlTicket>
<TicketDataSet xmlns="http://tempuri.org/Ticket.xsd">
<Ticket>
<TicketNumber>0123-456-789</TicketNumber>
...
</Ticket>
</TicketDataSet>
</xmlTicket>
XmlDocument.SelectSingleNode("//TicketNumber") returns null.
The problem is clearly the namespace. If I remove the xmlns= from the doc, the XPATHs work fine. If I use namespace neutral XPATH queries, they also work fine:
doc.SelectSingleNode("//*[local-name()='TicketNumber']");
But I can't do the former and I'd rather not do the latter.
I've found examples of how to configure a namespace manager, and tried to work out how to make this work for a default namespace.
This didn't work:
var nsm = new XmlNamespaceManager(doc.NameTable);
nsm.AddNamespace("", "http://tempuri.org/Ticket.xsd");
var ticketNumberNode = doc.SelectSingleNode("//TicketNumber", nsm);
And this didn't work:
var nsm = new XmlNamespaceManager(doc.NameTable);
nsm.AddNamespace("default", "http://tempuri.org/Ticket.xsd");
var ticketNumberNode = doc.SelectSingleNode("//default:TicketNumber", nsm);
Any ideas?
===
Note: I updated the XML to show more of the structure. The problem seems to be related to having default namespaces applied to parts of the document, rather than to its entirety.
Try 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);
string ticketNumber = (string)doc.Descendants().Where(x => x.Name.LocalName == "TicketNumber").FirstOrDefault();
}
}
}
The solution is strange but you should specify namespace with prefix in SelectionNamespaces property and specify this prefix in XPath queries. It doesn't matter if this namespace is default namespace with no prefix: you should use one. On the other side, you should add nodes to XML without prefix.
In your example the code should looks like this:
doc.setProperty("SelectionNamespaces", "xmlns:abc='http://tempuri.org/Ticket.xsd'");
doc.selectSingleNode("//abc:TicketNumber");

Selecting the value of XML elements

I don't have much experience with XML files but I'm trying to append a tutorial I found online to suite my needs and I'm not getting the results I would expect.
https://support.microsoft.com/en-us/help/307548/how-to-read-xml-from-a-file-by-using-visual-c
I've searched around but everything I've found doesn't make sense to me.
My XML looks like this for the most part:
<US>
<!-- Kentucky Start -->
<State>Kentucky KY
<City>Newport
<Street>Pavilion Parkway<Number>130<PostalCode>41071</PostalCode></Number></Street>
</City>
<City>Corbin
<Street>Highway 90<Number>7351<PostalCode>40701</PostalCode></Number></Street>
</City>
</State>
</US>
I'm trying to populate a listbox with the value of each state but my code either returns white space or just the text within the XML tag
e.g.
State..
State..
repeated for each element.
while (reader.Read()) {
switch (reader.NodeType) {
case XmlNodeType.Element: // The node is an element.
// Skip over root element
if (reader.Name.Equals("US")) {
reader.MoveToNextAttribute();
}
else {
if(reader.Name.Equals("State")) {
lbState.Items.Add(reader.Name);
lbState.Items.Add(reader.Value);
}
}
break;
reader.Name returns "State"
reader.Value returns "Whitespace"
I don't understand why reader.Value does not return Kentucky KY...
I've seen other examples that use string builder, is this a bad approach?
Use reader.ReadString() instead of reader.Value
Try xml linq :
sing System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication2
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("State").Select(x => new {
cities = x.Elements("City").Select(y => new {
state = (string)x,
city = (string)y,
streets = y.Elements("Street").Select(z => (string)z).ToList()
}).ToList()
}).SelectMany(x => x.cities).ToList();
}
}
}
You can use XmDocument (see: https://msdn.microsoft.com/en-us/library/system.xml.xmldocument(v=vs.110).aspx) and then use an xpath expression to get the right elements from your document:
Also it is better to encapsulate the name of the state (that is, if you own the xml document) like this:
<name>Kentucy KY</name>
So you can do the following:
var items = new List<string>();
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml("yourxml");
var xmlNodes = xmlDocument.SelectNodes("//State/Name");
foreach (XmlNode node in xmlNodes)
{
items.Add(xmlNode.Value);
}

Code provided to convert xml tags to attributes. Please explain. I'm a newbie

I need to convert xml tags to attributes so the following code loops through and does that BUT I'm a newbie. Just downloaded Visual Studio. I am used to actionscript so it's similar. However, I don't know how to paste in the code to make it work.
Converting XML nodes into attributes using C#
This is what I have so far. I pressed on new c# project and new class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ClassLibrary1
{
public class Class1
{
foreach (XElement el in root.Elements()) {
root.Add(new XAttribute(el.Name, (string)el)); }
root.Elements().Remove();
Console.WriteLine(root);
}
}
It's not really clear what you are asking here.
If you want to load an XDocument from an XML file, use XDocument.Load(). If you want to load an XDocument from a string containing XML, use XDocument.Parse().
If you want to promote the child elements of an XElement to be attributes of that element, you need to be aware that, unlike elements, duplicated attribute names are disallowed by the XML standard. You might also want to skip elements that contain nested child elements. If you don't, be aware that (string)el behaves as follows:
If the XElement has children, the concatenated string value of all of the element's text and descendant's text is returned.
That being said, the following promotes child elements to attributes, concatenating the values of identically named elements but doing nothing if there is already an attribute with the element's name:
public static void PromoteChildElementsToAttributes(XElement element)
{
foreach (var group in element.Elements().GroupBy(el => el.Name))
{
// Remove if you don't want this check.
if (element.Elements().Any())
{
//Uncomment if you want to skip elements with children.
//continue;
}
if (element.Attribute(group.Key) != null)
{
Debug.WriteLine("Cannot add duplicate attribute " + element.Attribute(group.Key));
continue;
}
var value = group.Aggregate(new StringBuilder(), (sb, el) => sb.Append((string)el)).ToString();
element.Add(new XAttribute(group.Key, value));
foreach (var el in group)
el.Remove();
}
}
And you could call it like:
var doc = XDocument.Load(fileName);
PromoteChildElementsToAttributes(doc.Root)

Read XML with ab:tag format using c#

I am new to xml, c#. I am following this tutorial: http://www.dotnetcurry.com/ShowArticle.aspx?ID=564
But my xml file is little different. The xml that I want to read in my c# code is this: http://api.nextag.com/buyer/synd.jsp?search=ipod&ver=15&token=AQB7dB$kB8ULvbGT&pid=1807
Code I am trying to read this xml is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
XElement xelement = XElement.Load("http://api.nextag.com/buyer/synd.jsp?search=ipod&ver=15&token=AQB7dB$kB8ULvbGT&pid=1807");
XNamespace nxtg = "http://schemas.microsoft.com/office/infopath/2003/myXSD/2011-01-11T08:31:30";
IEnumerable<XElement> employees = xelement.Elements();
// Read the entire XML
foreach (var employee in employees)
{
//Console.WriteLine(employee);
//Console.WriteLine(employee.Value);
if (employee.Element(nxtg + "search-category") == null)
continue;
else
Console.WriteLine(employee.Element(nxtg + "search-category").Value);
//Console.WriteLine(employee.Element("EmpId").Value);
}
But no luck. Anyone can help me please.
xelement.Elements() will return direct children of root element. In your case that will be elements nxtg:publisher, nxtg:search-query, nxtg:search-category etc. Thus nxtg:search-category is a direct child of root element, it also will selected as employee. That's why you can't find it in children of employee. You should do following instead:
// keep in mind, you have incorrect namespace value
XNamespace nxtg = "http://namespace.nextag.com/business-objects";
var searchCategory = xelement.Element(nxtg + "search-category");
var node = searchCategory.Element(nxtg + "node");
var displayName = (string)node.Element(nxtg + "display-name");
var value = (int)node.Element(nxtg + "value");

How to get specific Values from a xml file with same Name in a element?

I don't know how to extract values from this specific XML document, and am looking for some help as I'm not very experienced on xml parsing.
I have to use XDocument.Load to load the file.
Actually i am using
doc = XDocument.Load(uri);
challenge = GetValue(doc, "Challenge");
this works without any problems, but how to get the inner values of the Element Rights ? (multiple "Name")
At the end of the day i need to now
Phone = x
Dial = x
HomeAuto = x
BoxAdmin = x
It’s also possible that some of the entries (Phone,Dial,HomeAuto,BoxAdmin) is missing. This
is dynamic.
Here is my xml File:
<SessionInfo>
<SID>68eba0c8cef752a7</SID>
<Challenge>37a5fe9f</Challenge>
<BlockTime>0</BlockTime>
<Rights>
<Name>Phone</Name>
<Access>2</Access>
<Name>Dial</Name>
<Access>2</Access>
<Name>HomeAuto</Name>
<Access>2</Access>
<Name>BoxAdmin</Name>
<Access>2</Access>
</Rights>
</SessionInfo>
Edit: (Add GetValue method)
public string GetValue(XDocument doc, string name)
{
XElement info = doc.FirstNode as XElement;
return info.Element(name).Value;
}
NB: this solution uses extension methods, so the using directives are important or you won't see the required functions.
using System;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Collections.Generic;
namespace StackOverflow
{
class Program
{
const string xml = "<SessionInfo><SID>68eba0c8cef752a7</SID><Challenge>37a5fe9f</Challenge><BlockTime>0</BlockTime><Rights><Name>Phone</Name><Access>2</Access><Name>Dial</Name><Access>2</Access><Name>HomeAuto</Name><Access>2</Access><Name>BoxAdmin</Name><Access>2</Access></Rights></SessionInfo>";
static void Main(string[] args)
{
XDocument doc = XDocument.Parse(xml); //loads xml from string above rather than file - just to make it easy for me to knock up this sample for you
string nameOfElementToFind = "Name";
IEnumerable<XElement> matches = doc.XPathSelectElements(string.Format("//*[local-name()='{0}']",nameOfElementToFind));
//at this stage you can reference any value from Matches by Index
Console.WriteLine(matches.Count() > 2 ? "Third name is: " + matches.ElementAt(2).Value : "There less than 3 values");
//or can loop through
foreach (XElement match in matches)
{
Console.WriteLine(match.Value);
//or if you also wanted the related access info (this is a bit loose / assumes the Name will always be followed by the related Value
//Console.WriteLine("{0}: {1}", match.Value, match.XPathSelectElement("./following-sibling::*[1]").Value);
}
Console.WriteLine("Done");
Console.ReadKey();
}
}
}
The important bit here is the line IEnumerable<XElement> matches = doc.XPathSelectElements(string.Format("//*[local-name()=\'{0}\']",nameOfElementToFind));. After the string.format takes place the XPath is //*[local-name()='Name']. This XPath statement says to find all nodes with the name Name. The local-name() function's there because we haven't said what schema's being used, in this instance we want any element called Name, regardless of schema.
XmlNamespaceManager nm = new XmlNamespaceManager(new NameTable());
nm.AddNamespace("eg", "http://Example/Namespace/Replace/With/Your/Docs/Namespace");
IEnumerable<XElement> matches = document.XPathSelectElements("//eg:Name", nm);
The double forward-slash says to search anywhere in the document. To limit it to Rights you could say /eg:SessionInfo/eg:Rights/eg:Name. In case you're unfamiliar with it, XPath's an awesome language / essential if you want to get the most out of working with XML docs. If you have any questions about it please give us a shout, or have a look around online; there are great tutorials out there.

Categories

Resources