XML select sing node and loop through it - c#

I'm having some issues looping through an XML node.
To begin, here is the general format of the XML file:
<Config>
<Facilities>
<Facility>
<ID>1</ID>
<Name>Facility Name</Name>
<Profiles>
<Profile>
<Name>Value</Name>
<Item2>Value</2>
</Profile>
<Profile>
<Name>Another Value</Name>
</Profile>
</Profiles>
</Facility>
<Facility>
<ID>2</ID>
<Name>Facility Name</Name>
<Profiles>
<Profile>
<Name>Value</Name>
</Profile>
</Profiles>
</Facility>
</Facilities>
</Config>
And here's the code I'm using:
XmlDocument configFile = new XmlDocument();
configFile.Load(CONFIG_FILE);
XmlNodeList nodeList = configFile.SelectNodes("/Config/Facilities/Facility[ID[text()='" + facilityID + "']]/Profiles/Profile");
foreach (XmlNode no in nodeList)
{
Console.WriteLine(no["Name"].InnerText);
//myList.Items.Add(no["Name"].InnerText);
}
Basically, I want to loop through the facilities and select the facility with a specific ID (this id is derived from the facilityID that I set earlier in my code...and yes I've checked, it's being set correctly). I then want to go through the profiles associated with the facility and loop through them. I want to be able to take all the elements in each <Profile> as applicable.
However, when I execute this code, it's not returning anything in the debug console.

I'm not sure if this is exists in your file, but there appears to be XML errors in the example posted.
<Item2>Value</2>
If I remove that, I'm able to correctly see the value expected. For example, if I use a value of "1" for facilityID, I receive
Value
Another Value

You mean something like this? This is using the XElement class, of course. I tend to prefer it because it's easier to debug and, at least for me, easier to deploy.
var root = XElement.Load(CONFIG_FILE).Element("Facilities");
var facility = root.Elements("Facility").FirstOrDefault(c => c.Element("ID").Value == facilityID.ToString());
return facility.Element("Profiles").Elements("Profile");
It doesn't have any error handling, but you can add that in as necessary.
I don't know if you want to do this or not, it depends how you'll be using it, but you could also replace the last line there with this:
return facility.Element("Profiles").Elements("Profile").Select(c => c.Element("Name").Value);
And that would give you just the names. Again, depends on what exactly you're looking for. I just added this since I see your example is only printing out the names.

Related

Linq2Xml Remove specific attribute name and their values

I have this XML, and i am trying to delete those:
Name="items"
Name="shipmentsShipmentIndex"
"Name" is not going to change,
The word that will be change is the word between the quotation marks, in this example:
items and shipmentsShipmentIndex
<?xml version="1.0" encoding="UTF-8"?>
<RootDTO xmlns:json='http://james.newtonking.com/projects/json'>
<destination>
<name>XXX</name>
</destination>
<orderData>
<items json:Array='true'>
<shipmentIndex Name="items">0</shipmentIndex>
</items>
<items json:Array='true'>
<shipmentIndex Name="items">0</shipmentIndex>
</items>
<shipments json:Array='true'>
<shipmentIndex Name="shipmentsShipmentIndex">0</shipmentIndex>
</shipments>
</orderData>
</RootDTO>
How can i find all these occurrences and remove them(or replace them with nothing)?
I tried a bunch of variations like: Name{1}[a-zA-Z]+"\b
You wrote this is ASP.NET. You can use Linq2Xml to perform this operation very quickly (LinqPad example):
void Main()
{
var test = XDocument.Parse("<root><node name=\"name value\">Text</node></root>");
foreach(var attr in test.Descendants().SelectMany(el=>el.Attributes("name")))
attr.Remove();
test.Dump();
}
So from this:
<root>
<node name="name value">Text</node>
</root>
You get this:
<root>
<node>Text</node>
</root>
The snippet will remove ALL name attributes from every single element in the XML aside of the root one. If you want to do something else (like just empty the attribute, or include the root element), let me know.
Don't parse XML with regular expressions. Use an XML aware tool.
For example, in xsh, you can achieve this with
open file.xml ;
rm //#Name ;
save :b ;
Try this RegEx:
Name=\"(.*?)\"

Get value of an Xml attribute using Linq to Xml

I have an XML like this:
<?xml version="1.0" encoding="utf-8"?>
<Projects>
<Project>
<Name>Projekt0</Name>
<Information>
<Version>1.0</Version>
<Info>test-project</Info>
<CreateDate>25.02.2015</CreateDate>
</Information>
<Files ID="1" path="D:\Data\ID1">
<file>one_file</file>
<file>another_file</file>
</Files>
<Files ID="2" path="D:\Data\ID2">
<file>someFile.txt</file>
</Files>
</Project>
</Projects>
It contains some more "Project"-Nodes, but that's not necessary.
First, I select a specific project by it's name. This works already, and is done this way:
var entries = from items in xelement.Elements("Project")
where (string)items.Element("Name").Value == projectName
select items;
entries contains now all the content of the wanted project.
One of my methods need to know the path-values of the both "Files"-Nodes.
I already tried something, but it's not working yet.
My first try was creating a new 'var' like 'entries', converting that to an array, and selecting the indices for saving the values as a string.
var path1 = from item in entries.Elements("Files")
where (string)item.Attribute("ID").Value == "1"
select item.Attribute("path").Value;
string path01 = path1.toArray()[0];
That is not working, and I'm not sure why. I'm sorry if that is a beginners question, but I haven't done a lot with xml & linq yet.
edit:
The lower 'entries' variable is the output of the first linq-code. So 'entries' contains a whole project.
Currently, path1 does not contain any elements.
entries is a sequence of Project nodes. Take Files child nodes before searching ID attribute
var path1 = from item in entries.Elements("Files")
where (string)item.Attribute("ID").Value == "1"
select item.Attribute("path").Value;

Linq to XML - how do I get this element value

Fairly simple one, but my knowledge is limited in this area. I'm using the following c# code to access the value of elements within my SGML and XML documents.
It's working fine when there is only one element with the given name in the document, but as soon as there are more than one element with the same name it throws an exception, obviously!
I need to use xpath or some other way of specifying the location of the element i'm trying to get the value of.
XDocument doc = XDocument.Load(sgmlReader);
string system = doc.Descendants("chapnum").Single().Value;
return system;
This works fine, if there is only one "chapnum" in the doc, but i need to specifically get the value of "chapnum" at the following nested location "dmaddres/chapnum".
How please?
Here is a sample of the xml doc. I'm trying to get the value of the "chapnum" element nested in the "dmaddress" element.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dmodule []>
<dmodule xmlns:dc="http://www.purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.s1000d.org/S1000D_2-3-1/xml_schema_flat/descript.xsd">
<idstatus>
<dmaddres>
<dmc><avee><modelic>xx</modelic><sdc>A</sdc><chapnum>29</chapnum>
<section>1</section><subsect>3</subsect><subject>54</subject><discode
>00</discode><discodev>AAA</discodev><incode>042</incode><incodev
>A</incodev><itemloc>D</itemloc></avee></dmc>
<dmtitle><techname>Switch</techname><infoname>Description of function</infoname>
</dmtitle>
<issno inwork="00" issno="001" type="new"/>
<issdate day="20" month="07" year="2012"/>
<language language="sx"/></dmaddres>
<status>
<security class="01"/><datarest><instruct><distrib>-</distrib><expcont
>Obey the national regulations for export control.</expcont></instruct>
<inform><copyright><para><refdm><avee><modelic>xx</modelic><sdc>A</sdc>
<chapnum>29</chapnum><section>1</section><subsect>3</subsect><subject
>54</subject><discode>00</discode><discodev>ZZZ</discodev><incode
>021</incode><incodev>Z</incodev><itemloc>D</itemloc></avee></refdm
></para></copyright><datacond>BREXREF=AJ-A-00-00-00-05ZZZ-022Z-D VERSUB=CDIM-V6</datacond>
</inform></datarest>
<rpc>xxxxx</rpc>
<orig>xxxxx</orig>
<applic>
<type>-</type>
<model model="xxxxx"><mfc>xxxxx</mfc><pnr>xxxxxxx</pnr></model>
</applic>
<brexref><refdm><avee><modelic>xx</modelic><sdc>A</sdc><chapnum>00</chapnum>
<section>0</section><subsect>0</subsect><subject>00</subject><discode
>05</discode><discodev>ZZZ</discodev><incode>022</incode><incodev
>Z</incodev><itemloc>D</itemloc></avee></refdm></brexref>
like this?
string system = doc.Descendants("dmaddres")
.Single(e => e.Element("chapnum") != null)
.Element("chapnum").Value;
string system = doc.Root.Element("dmaddres").Element("chapnum").Value;
would probably do just as well.

XML Illegal Characters in path

I am querying a soap based service and wish to analyze the XML returned however when I try to load the XML into an XDoc in order to query the data. am getting an 'illegal characters in path' error message? This (below) is the XML returned from the service. I simply want to get the list of competitions and put them into a List I have setup. The XML does load into an XML Document though so must be correctly formatted?.
Any advice on the best way to do this and get round the error would be greatly appreciated.
<?xml version="1.0" ?>
- <gsmrs version="2.0" sport="soccer" lang="en" last_generated="2010-08-27 20:40:05">
- <method method_id="3" name="get_competitions">
<parameter name="area_id" value="1" />
<parameter name="authorized" value="yes" />
<parameter name="lang" value="en" />
</method>
<competition competition_id="11" name="2. Bundesliga" soccertype="default" teamtype="default" display_order="20" type="club" area_id="80" last_updated="2010-08-27 19:53:14" area_name="Germany" countrycode="DEU" />
</gsmrs>
Here is my code, I need to be able to query the data in an XDoc:
string theXml = myGSM.get_competitions("", "", 1, "en", "yes");
XmlDocument myDoc = new XmlDocument();
MyDoc.LoadXml(theXml);
XDocument xDoc = XDocument.Load(myDoc.InnerXml);
You don't show your source code, however I guess what you are doing is this:
string xml = ... retrieve ...;
XmlDocument doc = new XmlDocument();
doc.Load(xml); // error thrown here
The Load method expects a file name not an XML itself. To load an actual XML, just use the LoadXml method:
... same code ...
doc.LoadXml(xml);
Similarly, using XDocument the Load(string) method expects a filename, not an actual XML. However, there's no LoadXml method, so the correct way of loading the XML from a string is like this:
string xml = ... retrieve ...;
XDocument doc;
using (StringReader s = new StringReader(xml))
{
doc = XDocument.Load(s);
}
As a matter of fact when developing anything, it's a very good idea to pay attention to the semantics (meaning) of parameters not just their types. When the type of a parameter is a string it doesn't mean one can feed in just anything that is a string.
Also in respect to your updated question, it makes no sense to use XmlDocument and XDocument at the same time. Choose one or the another.
Following up on Ondrej Tucny's answer :
If you would like to use an xml string instead, you can use an XElement, and call the "parse" method. (Since for your needs, XElement and XDocument would meet your needs)
For example ;
string theXML = '... get something xml-ish...';
XElement xEle = XElement.Parse(theXML);
// do something with your XElement
The XElement's Parse method lets you pass in an XML string, while the Load method needs a file name.
Why not
XDocument.Parse(theXml);
I assume this will be the right solution
If this is really your output it is illegal XML because of the minus characters ('-'). I suspect that you have cut and pasted this from a browser such as IE. You must show the exact XML from a text editor, not a browser.

Is there any easy way to create new xmls from a xml file using c#

For example
<?xml version="1.0" encoding="UTF-8"?>
<!--just example, the actual element structure is rather complex -->
<components>
<component name='a'/>
<component name='b'/>
<component name='c'/>
</components>
Now I want to create three separate xml file, whose root element is component and the file name is according to the name attribute of them, How can I achieve this?
Regards,
Using LINQ to XML, something along the lines of
XElement rootElem = ... //load this however you happen to
foreach (var component in rootElem.Elements("component"))
component.Save(your-XmlWriter-here); //you'll want a distinct writer per file
should work fine; for your specific example...
string xml =#"
<components>
<component name='a'/>
<component name='b'/>
<component name='c'/>
</components>
";
foreach (XElement component in XElement.Parse(xml).Elements() )
component.Save(component.Attribute("name").Value + ".xml");
works exactly as specified. However, I find it to be good practice to avoid dealing with specific file names where possible, since often enough your XML can be processed directly later on in the pipeline without needing an intermediate (error-prone and slow) file save. For example, on a web-server it's not obvious you'll even have permissions to write anything, so you're reducing portability by forcing the usage of an actual file.
Here you go:
System.Xml.XmlDocument daddyDoc = new System.Xml.XmlDocument();
daddyDoc.LoadXml("<?xml version='1.0' encoding='UTF-8'?><components><component name='a'/><component name='b'/><component name='c'/></components>");
foreach (System.Xml.XmlNode sprogNode in daddyDoc.GetElementsByTagName("component"))
{
System.Xml.XmlDocument sprogDoc = new System.Xml.XmlDocument();
sprogDoc.CreateXmlDeclaration("1.0", "UTF-8", null);
sprogDoc.AppendChild(sprogDoc.CreateElement("component"));
sprogDoc.Save(string.Format("C:\\{0}.xml", sprogNode.Attributes["name"].Value));
}

Categories