XML :
<InformationTuples>
<InformationTuple>
<Name>documentClass</Name>
<value format="" valueset="{rechnung}" originalValue="Rechnung" start="0" end="0" LD="0" doc="C:\b4enviam-service-test\inputDir\031a0933-2616-4d8e-8a79-56746ae0e160/Invoice_51029062.pdf">Rechnung</value>
<EntityType>Class</EntityType>
<state>New </state>
<need>Mandatory </need>
<extractionmethod>
</extractionmethod>
<weight>1</weight>
<precondition type="optional">All</precondition>
</InformationTuple>
<InformationTuple>
<Name>SAPNr.</Name>
<value format="" valueset="" originalValue="4352020616" start="0" end="0" LD="0" doc="C:\b4enviam-service-test\inputDir\031a0933-2616-4d8e-8a79-56746ae0e160/Invoice_51029062.pdf">4352020616</value>
<EntityType>KB.GTInovice</EntityType>
<state>New </state>
<need>Mandatory </need>
<extractionmethod>
</extractionmethod>
<weight>1</weight>
<precondition type="optional">all</precondition>
</InformationTuple>
<InformationTuple>
<Name>GT-Invoice</Name>
<value format="" valueset="" originalValue="" start="0" end="0" LD="0" doc="">
</value>
<EntityType>KB.GTInovice</EntityType>
<state>New </state>
<need>Mandatory </need>
<extractionmethod>
</extractionmethod>
<weight>1</weight>
<precondition type="optional">all</precondition>
</InformationTuple>
</InformationTuples>
C#
reader.ReadToFollowing("InformationTuple");
reader2.ReadToFollowing("InformationTuple");
do
{
subtree = reader2.ReadSubtree();
subtree.ReadToFollowing("Name");
Debug.WriteLine(subtree.ReadElementContentAsString());
reader2.ReadToNextSibling("InfromationTuple");
} while (reader.ReadToNextSibling("InformationTuple"))
I'm trying for a while now to extract data from multiple childs in XML using c# but didn't successful. I have tried multiple code snippets but unable to extract data.
Like i have to extract the data given in three information tuples, but functions given in the XMLreader not working properly reader pointer break after single loop iteration (unable to move to second InformationTuple), even i have tried two different reader pointer but its now giving exception.
Need little help ,
Thanks
You can read the first <Name> element inside each <InformationTuple> as follows. Introduce the following extension methods:
public static partial class XmlReaderExtensions
{
public static IEnumerable<string> ReadAllElementContentsAsString(this XmlReader reader, string localName, string namespaceURI)
{
while (reader.ReadToFollowing(localName, namespaceURI))
yield return reader.ReadElementContentAsString();
}
public static IEnumerable<XmlReader> ReadAllSubtrees(this XmlReader reader, string localName, string namespaceURI)
{
while (reader.ReadToFollowing(localName, namespaceURI))
using (var subReader = reader.ReadSubtree())
yield return subReader;
}
}
And then do:
foreach (var name in reader.ReadAllSubtrees("InformationTuple", "")
.Select(r => r.ReadAllElementContentsAsString("Name", "").First()))
{
// Process the name somehow
Debug.WriteLine(name);
}
If you want to only read the first <Name> element of each <InformationTuple> element inside each <InformationTuples> container, you can restrict the scope of the search by composing calls to ReadAllSubtrees() using SelectMany():
foreach (var name in reader.ReadAllSubtrees("InformationTuples", "")
.SelectMany(r => r.ReadAllSubtrees("InformationTuple", ""))
.Select(r => r.ReadAllElementContentsAsString("Name", "").First()))
{
// Process the name somehow
Debug.WriteLine(name);
}
Some notes:
You don't close (or dispose) your ReadSubtree() subtree reader when you are done with it. From the docs:
You should not perform any operations on the original reader until the new reader has been closed. This action is not supported and can result in unpredictable behavior.
Thus you must close or dispose this nested reader before advancing the outer reader.
You have too many calls to reader.ReadToFollowing("InformationTuple"); at the beginning. Perhaps you meant to do reader.ReadToFollowing("InformationTuples");?
To ensure there is one and only one <Name> element for each <InformationTuple> replace First() with .Single().
If there might be multiple <Name> nodes for each <InformationTuple> and you want to read all of them, do:
foreach (var name in reader.ReadAllSubtrees("InformationTuple", "")
.SelectMany(r => r.ReadAllElementContentsAsString("Name", "")))
{
// Process the name somehow
Demo fiddle here.
The code below I used a lot and will not create any errors.
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)
{
XmlReader reader = XmlReader.Create(FILENAME);
while (!reader.EOF)
{
if (reader.Name != "InformationTuple")
{
reader.ReadToFollowing("InformationTuple");
}
if (!reader.EOF)
{
XElement subtree = (XElement)XElement.ReadFrom(reader);
Info.info.Add(new Info() { state = (string)subtree.Element("state"), weight = (int)subtree.Element("weight") });
}
}
}
}
public class Info
{
public static List<Info> info = new List<Info>();
public string state { get; set; }
public int weight { get; set; }
}
}
Related
I have tried to setup a Dialogue tree within unity using XML (I have not used XML much before so am unsure if the way i am going is correct at all)
So I am trying to get the first text element from this dialogue tree but when i call the XML file and say where it is i am getting the everything stored in that branch.
Am i using the correct .XML to be able to do this also as i seen people say use .XML.LINQ or .XML.Serialization not just .XML is this correct for my case ??
Code:
using UnityEngine;
using System.Collections;
using System.IO;
using System.Xml;
using UnityEngine.UI;
using System.Collections.Generic;
public class DialogTree
{
public string text;
public List<string> dialogText;
public List<DialogTree> nodes;
public void parseXML(string xmlData)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(new StringReader(xmlData));
XmlNode node = xmlDoc.SelectSingleNode("dialoguetree/dialoguebranch");
text = node.InnerXml;
XmlNodeList myNodeList = xmlDoc.SelectNodes("dialoguebranch/dialoguebranch");
foreach (XmlNode node1 in myNodeList)
{
if (node1.InnerXml.Length > 0)
{
DialogTree dialogtreenode = new DialogTree();
dialogtreenode.parseXML(node1.InnerXml);
nodes.Add(dialogtreenode);
}
}
}
}
And here is a picture of the XML.
So i am trying to grab the first element of text then late on there response it will go to branch 1 or 2
<?xml version='1.0'?>
<dialoguetree>
<dialoguebranch>
<text>Testing if the test prints</text>
<dialoguebranch>
<text>Branch 1</text>
<dialoguebranch>
<text>Branch 1a</text>
</dialoguebranch>
<dialoguebranch>
<text>Branch 1b</text>
</dialoguebranch>
</dialoguebranch>
<dialoguebranch>
<text>Branch 2</text>
</dialoguebranch>
</dialoguebranch>
</dialoguetree>
You're getting everything in that branch because XmlNode.InnerXML returns everything in that node. See the documentation for more information on that.
You should use the branch as the base for only looking at its children, instead of starting at xmlDoc every time. Also, you need an entry point to get inside of the first dialoguetree element and then ignore that. Finally, I would only create one XmlDocument and just pass around nodes in your recursion.
Altogether, this might look like this:
public class DialogTree
{
public string text;
public List<DialogTree> nodes = new List<DialogTree>();
public static DialogTree ParseXMLStart(string xmlData)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(new Stringreader(xmlData));
XmlNode rootNode = xmlDoc.SelectSingleNode("dialoguetree/dialoguebranch");
DialogTree dialogTree = new DialogTree();
dialogTree.ParseXML(rootNode);
return dialogTree;
}
public void ParseXML(XmlNode parentNode)
{
XmlNode textNode = parentNode.SelectSingleNode("text");
text = textNode.InnerText;
XmlNodeList myNodeList = parentNode.SelectNodes("dialoguebranch");
foreach (XmlNode curNode in myNodeList)
{
if (curNode.InnerXml.Length > 0)
{
DialogTree dialogTree = new DialogTree();
dialogTree.ParseXML(curNode);
nodes.Add(dialogTree);
}
}
}
}
And you could use it like so:
string xmlStringFromFile;
DialogTree dialogue = DialogTree.ParseXMLStart(xmlStringFromFile);
All of this code is untested but I hope the general idea is clear. Let me know if you find any errors in the comments below and I will try to fix them.
I'm trying to parse/get the information of an XML file where I have saved the setting values.
I would like to open a dialog, where the user can select the .xml file and after that get the information and load the settings.
The XML file looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<Configuration version="1.2" createDate="2018-07-17T10:00:00">
<AutoScale>1</Autoscale>
<Threshold>2142</Threshold>
<MinAuto>14</MinAuto>
<MinMan>1</MinMan>
<MaxMan>1</MaxMan>
<BlueBackground>1</BlueBackground>
<Contour>1</Contour>
<Rotate>180</Rotate>
<Flip>Vertical</Flip>
</Configuration>
My code (in C#) looks like this:
using (var openFileDialogXML = new OpenFileDialog()){
System.IO.Stream myStream = null;
openFileDialogXML.InitialDirectory = #System.Environment.CurrentDirectory;
openFileDialogXML.Filter = "xml files (*.xml)|*.xml|All files (*.*)|*.*";
openFileDialogXML.FilterIndex = 1;
openFileDialogXML.RestoreDirectory = true;
DialogResult dr = openFileDialogXML.ShowDialog();
if (dr == System.Windows.Forms.DialogResult.OK)
{
using (XmlReader reader = XmlReader.Create(openFileDialogXML.FileName))
{
reader.MoveToContent();
var version = reader.GetAttribute("version");
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "AutoScale":
//Get AutoScale value
break;
case "Threshold":
break;
case "MinAuto":
break;
case "MinMan":
break;
case "MaxMan":
break;
}
}
}
}
I'm open to use any parser but I would like to read it element by element because it could happen that we add new settings in the future.
Can you please help me/ give me some advice about how I can reach this?
I like using Xml Linq and putting results into a dictionary so when new items are added the xml parser doesn't have to change :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication53
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Dictionary<string, string> dict = doc.Element("Configuration").Elements()
.GroupBy(x => x.Name.LocalName, y => (string)y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
}
I would suggest to use DataContract and load the XML into specified object. When your configuration file changes, you would need to update also the Entity.
[DataContract]
public class MyXmlClass
{
[DataMember]
public int PropertyToSerialize { get; set; }
}
You can then use DataContractSerializer as described here - https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/serialization-and-deserialization
It will be much easier for you to work with object than parsing XML manually :)
Some quick and dirty answer if you want to parse it manually:
using System.Xml;
[...]
XmlTextReader xtr = new XmlTextReader(GetResourceStream("config.xml"));
while (xtr.Read())
{
if (xtr.AttributeCount == 0)
continue;
if (xtr.LocalName == "Configuration")
{
string version = xtr.GetAttribute("version");
string date = xtr.GetAttribute("createDate");
Console.WriteLine($"version={version} - date = {date}")
}
else if (xtr.LocalName == "AutoScale")
{
string autoscale = xtr.ReadString();
Console.WriteLine($"autoscale={autoscale}")
}
[...]
}
xtr.Close();
I didn't try the code, if you need more start by looking XmlTextReader examples or documentation (stackoverflow should have plenty of them)
How to parse all the XML files under a given directory as an input to the application and write its output to a text file.
Note: The XML is not always the same the nodes in the XML can vary and have any number of Child-nodes.
Any help or guidance would be really helpful on this regard :)
XML File Sample
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>
<CNT>USA</CNT>
<CODE>3456</CODE>
</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</CD>
</CATALOG>
C# Code
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Data;
using System.Xml;
using System.Xml.Linq;
namespace XMLTagParser
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please Enter the Location of the file");
// get the location we want to get the sitemaps from
string dirLoc = Console.ReadLine();
// get all the sitemaps
string[] sitemaps = Directory.GetFiles(dirLoc);
StreamWriter sw = new StreamWriter(Application.StartupPath + #"\locs.txt", true);
// loop through each file
foreach (string sitemap in sitemaps)
{
try
{
// new xdoc instance
XmlDocument xDoc = new XmlDocument();
//load up the xml from the location
xDoc.Load(sitemap);
// cycle through each child noed
foreach (XmlNode node in xDoc.DocumentElement.ChildNodes)
{
// first node is the url ... have to go to nexted loc node
foreach (XmlNode locNode in node)
{
string loc = locNode.Name;
// write it to the console so you can see its working
Console.WriteLine(loc + Environment.NewLine);
// write it to the file
sw.Write(loc + Environment.NewLine);
}
}
}
catch {
Console.WriteLine("Error :-(");
}
}
Console.WriteLine("All Done :-)");
Console.ReadLine();
}
}
}
Preferred Output:
CATALOG/CD/TITLE
CATALOG/CD/ARTIST
CATALOG/CD/COUNTRY/CNT
CATALOG/CD/COUNTRY/CODE
CATALOG/CD/COMPANY
CATALOG/CD/PRICE
CATALOG/CD/YEAR
CATALOG/CD/TITLE
CATALOG/CD/ARTIST
CATALOG/CD/COUNTRY
CATALOG/CD/COMPANY
CATALOG/CD/PRICE
CATALOG/CD/YEAR
This is a recursive problem, and what you are looking for is called 'tree traversal'. What this means is that for each child node, you want to look into it's children, then into that node's children (if it has any) and so on, recording the 'path' as you go along, but only printing out the names of the 'leaf' nodes.
You will need a function like this to 'traverse' the tree:
static void traverse(XmlNodeList nodes, string parentPath)
{
foreach (XmlNode node in nodes)
{
string thisPath = parentPath;
if (node.NodeType != XmlNodeType.Text)
{
//Prevent adding "#text" at the end of every chain
thisPath += "/" + node.Name;
}
if (!node.HasChildNodes)
{
//Only print out this path if it is at the end of a chain
Console.WriteLine(thisPath);
}
//Look into the child nodes using this function recursively
traverse(node.ChildNodes, thisPath);
}
}
And then here is how I would add it into your program (within your foreach sitemap loop):
try
{
// new xdoc instance
XmlDocument xDoc = new XmlDocument();
//load up the xml from the location
xDoc.Load(sitemap);
// start traversing from the children of the root node
var rootNode = xDoc.FirstChild;
traverse(rootNode.ChildNodes, rootNode.Name);
}
catch
{
Console.WriteLine("Error :-(");
}
I made use of this other helpful answer: Traverse a XML using Recursive function
Hope this helps! :)
I have been wrestling with deserializing the following XML document:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:settings xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main" mc:Ignorable="w14 w15">
<w:zoom w:percent="100"></w:zoom>
<w:proofState w:spelling="clean" w:grammar="clean"></w:proofState>
<w:defaultTabStop w:val="720"></w:defaultTabStop>
<w:characterSpacingControl w:val="doNotCompress"></w:characterSpacingControl>
<w:compat>
<w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="15"></w:compatSetting>
<w:compatSetting w:name="overrideTableStyleFontSizeAndJustification" w:uri="http://schemas.microsoft.com/office/word" w:val="1"></w:compatSetting>
<w:compatSetting w:name="enableOpenTypeFeatures" w:uri="http://schemas.microsoft.com/office/word" w:val="1"></w:compatSetting>
<w:compatSetting w:name="doNotFlipMirrorIndents" w:uri="http://schemas.microsoft.com/office/word" w:val="1"></w:compatSetting>
<w:compatSetting w:name="differentiateMultirowTableHeaders" w:uri="http://schemas.microsoft.com/office/word" w:val="1"></w:compatSetting>
</w:compat>
<w:rsids>
<w:rsidRoot w:val="00B31FC7"></w:rsidRoot>
<w:rsid w:val="00251096"></w:rsid>
<w:rsid w:val="00481AA7"></w:rsid>
<w:rsid w:val="005C6856"></w:rsid>
<w:rsid w:val="00661DE2"></w:rsid>
<w:rsid w:val="00984D97"></w:rsid>
<w:rsid w:val="00A06ADC"></w:rsid>
<w:rsid w:val="00B31FC7"></w:rsid>
</w:rsids>
<m:mathPr>
<m:mathFont m:val="Cambria Math"></m:mathFont>
<m:brkBin m:val="before"></m:brkBin>
<m:brkBinSub m:val="--"></m:brkBinSub>
<m:smallFrac m:val="0"></m:smallFrac>
<m:dispDef></m:dispDef>
<m:lMargin m:val="0"></m:lMargin>
<m:rMargin m:val="0"></m:rMargin>
<m:defJc m:val="centerGroup"></m:defJc>
<m:wrapIndent m:val="1440"></m:wrapIndent>
<m:intLim m:val="subSup"></m:intLim>
<m:naryLim m:val="undOvr"></m:naryLim>
</m:mathPr>
<w:themeFontLang w:val="en-US"></w:themeFontLang>
<w:clrSchemeMapping w:bg1="light1" w:t1="dark1" w:bg2="light2" w:t2="dark2" w:accent1="accent1" w:accent2="accent2" w:accent3="accent3" w:accent4="accent4" w:accent5="accent5" w:accent6="accent6" w:hyperlink="hyperlink" w:followedHyperlink="followedHyperlink"></w:clrSchemeMapping>
<w:shapeDefaults>
<o:shapedefaults v:ext="edit" spidmax="1026"></o:shapedefaults>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1"></o:idmap>
</o:shapelayout>
</w:shapeDefaults>
<w:decimalSymbol w:val="."></w:decimalSymbol>
<w:listSeparator w:val=","></w:listSeparator>
<w15:chartTrackingRefBased></w15:chartTrackingRefBased>
<w15:docId w15:val="{23720E07-DD19-46BC-8098-ED32713AB32B}"></w15:docId>
</w:settings>
I am only interested in what is contained within the rsids element. So I thought I could create classes that looked like this:
[XmlRoot(ElementName ="settings", Namespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main")]
public class rsids
{
[XmlElement(ElementName ="rsids",Namespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main")]
public List<rsid> Rsids { get; set; }
}
public class rsid
{
[XmlAttribute(Namespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main")]
public static string val { get; set; }
}
I am attempting to deserialize like this:
XDocument xdoc = XDocument.Load(file);
using (TextReader reader = new StringReader(xdoc.ToString()))
{
try
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(rsids));
StreamReader sr = new StreamReader(file);
rsids SettingsXml = (rsids)xmlSerializer.Deserialize(sr);
foreach (var rsid in SettingsXml.Rsids)
{
Console.WriteLine(rsid.val.Count());
}
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.ReadLine();
}
}
However, I am getting the following error: "Value cannot be null". This is my first attempt at deserializing an XML document with Namespaces. I have browsed the community and found plenty of articles of folks having similar issues however, after trying some of those solutions I am just as confused as when I started and just going in circles. I want to understand this. Some of those posted solutions out there seem to indicate I only have to add a blank Namespace attribute to my decorators (Namespace ="") and others show the actual namespace uri being referenced but only for the root element leaving blanks in subsequent elements \ attributes. I am more looking for the education as to 'why'\'when' to use one method over another and an example of how to accomplish this given the XML below. I appreciate any help you can provide.
Cheers
You're not too far off.
Your XmlElement attribute implies multiple rsids elements. What you want is a single rsids element containing multiple rsid elements. The easiest way to do this is using the XmlArray and XmlArrayItem attributes.
The val property shouldn't be static
Due to what looks like a bug in XmlSerializer, you need to include Form = XmlSchemaForm.Qualified in your XmlAttribute attribute.
You can also omit most of your Namespace properties as they'll be inherited, and ElementName doesn't have to be specified explicitly.
Putting all that together:
[XmlRoot(Namespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main")]
public class settings
{
[XmlArray("rsids")]
[XmlArrayItem("rsid")]
public List<rsid> Rsids { get; set; }
}
public class rsid
{
[XmlAttribute(Form = XmlSchemaForm.Qualified)]
public string val { get; set; }
}
Of course, if that's all you want then a simple LINQ to XML query would be a lot easier:
XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
var rsids = doc.Descendants(w + "rsid")
.Attributes(w + "val")
.Select(x => x.Value);
See this fiddle for a working demo of both approaches.
Try this
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);
int[] rsids = doc.Descendants().Where(x => x.Name.LocalName == "rsids").Select(x => new {
rsids = x.Elements().Select(y => int.Parse((string)y.Attribute(x.GetNamespaceOfPrefix("w") + "val"), System.Globalization.NumberStyles.HexNumber))
}).Select(x => x.rsids).FirstOrDefault().Select(x => x).ToArray();
}
}
}
Thank you very much for reading my question.
the bottom is the sample of my xml file.please refer that.
i did some xml files before, but by "CMarkXml". "IntoElement, OutofElement", is very clear.
but when C#...i was lost..
1: how to read & write my xml file without using the tag name. i see some articles about operation on xml file by c#, but all assumed that known the tag name.
2: if without tag name, it is very difficult or not recommend. then how to read & write my xml file by XmlDocument? (sorry, but no Ling please, i am very faint with that...).
3: my idear is, for the xml file, get out some section, we still could parse the section by xmldocument.
4: for the write/modify the xml file, of course, should contain delete some section, delete some "leaf", change the attributes...
Thank you very much for reading the long question, and any help i will very appreciate. If you have a good sample code but not continent paste them here, could you send it to "erlvde#gmail.com"?
<root>
<a>i belong to a</a>
<b>
<bb>
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
</bb>
<bb>
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
<bb>
....(other <bb>)
</b>
</root>
Read your xml into XmlDocument:
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml("XML HERE");
Access child nodes:
xmlDocument.ChildNodes[1]
But it's also true that it's very error prone
You can also check if you have child nodes at all:
xmlDocument.HasChildNodes
And get number of child nodes:
xmlDocument.ChildNodes.Count
It looks to me like your elements names contain identifiers. If that is the case, and you have control over the XML schema, I would highly recommend changing your XML to contain elements and/or attributes indicating your identifiers and then use the built in XmlSerializer class for serializing to and from XML. It has many modifiers available, such as XmlElement and XmlAttribute among many others, for formatting the output.
Here is a tutorial to get you started.
If possible, change your XML to something like following which would make it far simpler to manipulate...again if changing the schema is a possibility.
<root>
<a>i belong to a</a>
<b>
<bb id="1">
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
</bb>
<bb id="2">
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
<bb>
</b>
</root>
Edit this edit reflects the changes you made to your XML
Here is a simple console application which will serialize an object to an XML file and then rehydrate it.
Expected XML
<?xml version="1.0" encoding="utf-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<a>i belong to a</a>
<b>
<bb>
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
</bb>
<bb>
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
</bb>
</b>
</root>
Simple Console Application Demonstration
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var items = new root
{
a = "i belong to a",
b = new List<bb>
{
new bb
{
bbClassProperty = new List<int>
{
1,
2,
3,
4,
5
}
},
new bb
{
bbClassProperty= new List<int>
{
1,
2,
3,
4,
5
}
}
}
};
XmlSerializer serializer = new XmlSerializer(typeof(root));
using (var textWriter = new StreamWriter(#"C:\root.xml"))
{
serializer.Serialize(textWriter, items);
textWriter.Close();
}
using (var stream = new StreamReader(#"C:\root.xml"))
{
var yourObject = serializer.Deserialize(stream);
}
Console.Read();
}
}
#region [Classes]
public class root
{
public string a { get; set; }
public List<bb> b { get; set; }
}
public class bb
{
[XmlElement("bb")]
public List<int> bbClassProperty { get; set; }
}
#endregion
}
Look into the ChildNodes (and similar) properties and methods on your XmlElement object. These will let you iterate over the children of a node and you can then ask that node for its name.
If you have a XmlNode object, you can use XMLNode.FirstChild to get the child, if it has any. You can also use XMLNode.NextSibling to get the next Node of the same parent node.
Why can't you use the names of the nodes? It's the easiest and most common way. Especially if you use XPath or similar.
XPath is also the answer to your second question.
U can use the class XML reader, a simple example is given here.
using System;
using System.Xml;
class Program
{
static void Main()
{
// Create an XML reader for this file.
using (XmlReader reader = XmlReader.Create("perls.xml"))
{
while (reader.Read())
{
// Only detect start elements.
if (reader.IsStartElement())
{
// Get element name and switch on it.
switch (reader.Name)
{
case "perls":
// Detect this element.
Console.WriteLine("Start <perls> element.");
break;
case "article":
// Detect this article element.
Console.WriteLine("Start <article> element.");
// Search for the attribute name on this current node.
string attribute = reader["name"];
if (attribute != null)
{
Console.WriteLine(" Has attribute name: " + attribute);
}
// Next read will contain text.
if (reader.Read())
{
Console.WriteLine(" Text node: " + reader.Value.Trim());
}
break;
}
}
}
}
}
}
The input file text is:
<?xml version="1.0" encoding="utf-8" ?>
<perls>
<article name="backgroundworker">
Example text.
</article>
<article name="threadpool">
More text.
</article>
<article></article>
<article>Final text.</article>
</perls>
Output
Start element.
Start element.
Has attribute name: backgroundworker
Text node: Example text.
Start element.
Has attribute name: threadpool
Text node: More text.
Start element.
Text node:
Start element.
Text node: Final text.enter code here
You can use the following code to if the file does not contain the headers, in the example above.
XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;
reader = XmlReader.Create(filePath, settings)
Would something like this help?
void Iterate(XmlNode parent) {
//do something with
//parent.Name
//parent.Value
//parent.Attributes
foreach(XmlNode child in parent.ChildNodes) {
Iterate(child);
}
}
XmlDocument document = new XmlDocument();
document.Load(filename);
XmlNode parent = document.DocumentElement;
Iterate(parent);
You could also store it like that (sorry for any syntactical error, didn't run it)
public class Document {
public Element DocumentElement { set; get; }
private void Load(string fileName) {
XmlDocument document = new XmlDocument();
document.Load(fileName);
DocumentElement = new Element(this, null);
DocumentElement.Load(document.DocumentElement);
}
}
public class Element {
public string Name { set; get; }
public string Value { set; get; }
//other attributes
private Document document = null;
private Element parent = null;
public Element Parent { get { return parent; } }
public List<Element> Children { set; get; }
private int order = 0;
public Element(Document document, Element parent) {
Name = "";
Value = "";
Children = new List<LayoutElement>();
this.document = document;
this.parent = parent;
order = parent != null ? parent.Children.Count + 1 : 1;
}
private Element GetSibling(bool left) {
if(parent == null) return null;
int add = left ? -1 : +1;
Element sibling = parent.Children.Find(child => child.order == order + add);
return sibling;
}
public Element GetLeftSibling() {
return GetSibling(true);
}
public Element GetRightSibling() {
return GetSibling(false);
}
public void Load(XmlNode node) {
Name = node.Name;
Value = node.Value;
//other attributes
foreach(XmlNode nodeChild in node.Children) {
Element child = new Element(document, this);
child.Load(nodeChild);
Children.Add(child);
}
}
}
Document document = new Document();
document.Load(fileName);
For changing/deleting right now you could iterate the tree and find elements by name, but since name is not unique, you would affect many elements at once. You could add an unique id in every tag like
<bb id="bb1"/>
Then read it in Load function like
id = ((XmlElement)node).GetAttribute("id");
and use this id to iterate through the tree. Sorry I don't have time right now to provide something more detailed.