What needs to be done
Currently I have 1 Advanced XML file that needs to be parsed. I need to iterate through the file and read each "Entity" tag individually. Altough the issue I come across is reading & iterating through the Stats and Slots. Also, the amount of Stat & Slot tags will vary depending on the Entity. (Yes I have researched this topic but I still can't find a way without creating errors as I need some more guidance. The other posts haven't had the exact fix I've hoped for...)
XML File
<ROOT>
<Entity Type="Clothing" Name="Light Robe" ID="0">
<Armor>2</Armor>
<Weight>1</Weight>
<Usability>120</Usability>
<Rarity>0.1</Rarity>
<Stats>
<Stat Type="Health">10</Stat>
</Stats>
<Slots>
<Slot>Torso</Slot>
</Slots>
</Entity>
<Entity Type="Clothing" Name="Medium Robe" ID="1">
<Armor>4</Armor>
<Weight>2</Weight>
<Stats>
<Stat Type="Health">15</Stat>
</Stats>
<Usability>120</Usability>
<Rarity>0.1</Rarity>
<Slots>
<Slot>Torso</Slot>
</Slots>
</Entity>
<Entity Type="Clothing" Name="Heavy Robe" ID="2">
<Armor>6</Armor>
<Weight>4</Weight>
<Stats>
<Stat Type="Health">25</Stat>
</Stats>
<Usability>120</Usability>
<Rarity>0.1</Rarity>
<Slots>
<Slot>Torso</Slot>
</Slots>
</Entity>
</ROOT>
If anybody has any criticism of this post please say so as I'll edit accordingly.
Use XDocument for XML reading.
// Load Document
XDocument _doc = XDocument.Load("C:\\t\\My File2.txt");
// Get all Entity elements and put them into a list.
List<XElement> employees = _doc.XPathSelectElements("ROOT/Entity").ToList();
// Next you can loop thru the list to check the Entity's elements
foreach (var employee in employees)
{
// to get the armor element:
string armor = employee.Element("Armor").Value;
// to get the rarity element:
string rarity = employee.Element("Rarity").Value;
// to get the Stat element:
string stat = employee.Element("Stats").Element("Stat").Value;
// to get the Slot element:
string slot = employee.Element("Slots").Element("Slot").Value;
}
// To get one element specific by attribute use this (I check on attribute ID):
XElement emp = _doc.XPathSelectElements("ROOT/Entity").FirstOrDefault(c => c.Attribute("ID").Value == "0");
// Next you can extract information from this element just like in the foreach loop.
Related
I have an example of my XML below (which i'm converting from SGML to XML prior to the check but that's besides the point). The XML IS valid at the point of checking.
I need check the XDocument and find out if any of the <version> elements contained within the <status> section at the top of the file contain the attriubute "RNWB". I need to be explicit about the fact that i'm only interested in checking <version> elements that are children of <status> elements because there might be other <version> elements within the document which i don't care about.
<dmodule>
<idstatus>
<dmaddres>
<dmc>Blah</dmc>
<dmtitle><techname>System</techname><infoname>Introduction</infoname></dmtitle>
<issno issno="006" type="revised"/>
<issdate year="2016" month="11" day="30"/>
</dmaddres>
<status>
<security class="2"/>
<rpc></rpc>
<orig></orig>
<applic>
<model model="2093">
<version version="BASE"></version>
<version version="RNWB"></version></model>
</applic>
<techstd>
<autandtp>
<authblk></authblk>
<tpbase></tpbase>
</autandtp>
<authex></authex>
<notes></notes>
</techstd>
<qa>
<firstver type="tabtop"/></qa>
<remarks></remarks>
</status>
</idstatus>
<content>
<refs><norefs></refs>
<descript>
<para0><title>Introduction</title>
<para>The system many new features included which are safe and accurate blah blah blah.</para>
</descript>
</content>
</dmodule>
I've tried all sorts but can't seem to get a result. Here's one example of what i've tried:
var result = (from ele in doc.Descendants("applic").Descendants("version").Attributes("RNWB")
select ele).ToList();
foreach (var v in result)
{
File.Move(file, Path.Combine(outputFolder, fileName)); // move the file to the new folder
}
If you have to be explicit that the version element is within a status element, then you need to be explicit about that in your query too. Your example doesn't include version anywhere.
You then need to find a version attribute with the value RNWB. You're currently looking for an RNWB attribute, which doesn't exist.
var hasFlag = doc
.Descendants("status")
.Descendants("version")
.Attributes("version")
.Any(x => x.Value == "RNWB");
See this fiddle for a demo.
Something like this should work:
// Load document
XDocument _doc = XDocument.Load("C://t//My File2.txt");
Select path to model
Select the elements version
Where the attribute == RNWB
Put them in a list for the result
// if you need to get the version element where the atrribute == something
List<XElement> model = _doc
.XPathSelectElement("dmodule/idstatus/status/applic/model")
.Elements("version")
.Where(x => x.Attribute("version").Value == "RNWB")
.ToList();
// if you only need to check if the attribute exists
bool contains = _doc
.XPathSelectElement("dmodule/idstatus/status/applic/model")
.Elements("version")
.Any(x => x.Attribute("version").Value == "RNWB");
The XML is not in valid format: I made some adjustment and got it working
<dmodule>
<idstatus>
<dmaddres>
<dmc>Blah</dmc>
<dmtitle><techname>System</techname><infoname>Introduction</infoname></dmtitle>
<issno issno="006" type="revised"/>
<issdate year="2016" month="11" day="30"/>
</dmaddres>
<status>
<security class="2"/>
<rpc></rpc>
<orig></orig>
<applic>
<model model="2093">
<version version="BASE"></version>
<version version="RNWB"></version>
</model>
</applic>
<techstd>
<autandtp>
<authblk></authblk>
<tpbase></tpbase>
</autandtp>
<authex></authex>
<notes></notes>
</techstd>
<qa>
<firstver type="tabtop"/></qa>
<remarks></remarks>
</status>
</idstatus>
<content>
<refs>
<norefs>
</norefs>
</refs>
<descript>
<para0>
<title>Introduction</title>
<para>The system many new features included which are safe and accurate blah blah blah.</para>
</para0>
</descript>
</content>
</dmodule>
I want to read all the nodes in the xml. Reading the top node without the loop goes fine, but I want to read all the nodes to get all the different temperatures for each city. This is the xml:
<servers>
<server name ="Stockholm">
<!-- data from Google weather -->
<name>Stockholm (Google)</name><!--Läser denna rad för tillfället-->
<url>http://www.yr.no/place/sweden/stockholm/stockholm/forecast.xml</url>
<xpath>/weatherdata/forecast/tabular/time/temperature</xpath>
</server>
<server name ="Göteborg">
<name>Göteborg (Google)</name> <url>http://www.yr.no/place/Sweden/V%C3%A4stra_G%C3%B6taland/Gothenburg/forecast.xml</url>
<xpath>/weatherdata/forecast/tabular/time/temperature</xpath>
</server>
<server name =" Malmö">
<name>Malmö (Google)</name>
<url>http://www.yr.no/place/sweden/scania/malmö/forecast.xml</url>
<xpath>/weatherdata/forecast/tabular/time/temperature</xpath>
</server>
</servers>
My code so far:
XmlDocument serverDoc = new XmlDocument();
serverDoc.Load("ServerUrls.xml");
XmlNodeList xml =
serverDoc.SelectNodes("servers/server");
foreach (var node in xml)
{
}
I know its not much. But cant seem to find information I can use properly. Been to MSDN and tried to figure it out from there, but to no result.
Thankful for all the help I can get.
As mentioned in the comment, you need to get URL of the XML where the actual temperature resides, as well as the corresponding XPath to locate the temperatures within the XML. Load XML from the URL into XmlDocument and then execute the XPath to extract the actual temperature values :
foreach (XmlNode node in xml)
{
// get information needed to extract temprature i.e XML location, and the XPath :
var loc = node.SelectSingleNode("url").InnerText;
var xpath = node.SelectSingleNode("xpath").InnerText;
// get temperature information
var doc = new XmlDocument();
doc.Load(loc);
var temperatures = doc.SelectNodes(xpath);
// iterate through temperatures and take action accordingly, f.e print the temperature
foreach(XmlNode temperature in temperatures)
{
Console.WriteLine(temperature.Attributes["value"].Value);
}
}
I have the following XML
<ABC xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://ns.hr-xml.org/2007-04-15">
<ReceiptId>
<IdValue>123</IdValue>
</ReceiptId>
<ClientOrderId>
<IdValue>345</IdValue>
</ClientOrderId>
<AccessPoint>
<Description>My Description</Description>
</AccessPoint>
<ABCStatus>
<Status>Error</Status>
<Details>ERRORS:
Talent is already in an active process for this opening.
</Details>
<StatusDate>2015-08-05</StatusDate>
</ABCStatus>
</ABC>
I am trying to retrieve the element value 345 nested in IdValue and ClientOrderId
I have used the Linq to xml code in C# to retrieve the value with no luck
XDocument XMLResults = XDocument.Parse(sResult);
var sClientOrderID =
from nodeAElem in XMLResults.Root.Elements("ABC")
from nodeA1Elem in nodeAElem.Elements("ClientOrderId")
from nodeA11Elem in nodeA1Elem.Elements("IdValue")
select nodeA11Elem.Value;
also need to retrieve the Status Elements value which is Error for the above xml.
Any help is greatly appreciated
Your XML document is using a namespace, you have to use it in your query to make it work.
Root already brings you to ABC element, so you don't have to call Elements("ABC")
You're looking for single value, so you probably want to use Element instead of Elements.
var ns = (XNamespace)"http://ns.hr-xml.org/2007-04-15";
var sClientOrderID = (int)XMLResults.Root
.Element(ns + "ClientOrderId")
.Element(ns + "IdValue");
I have an xml file loaded into an XDocument that I need to extract a value from, and I'm not sure of the best way to do it. Most of the things I'm coming up with seem to be overkill or don't make good use of xml rules. I have the following snippet of xml:
<entry>
<observation classCode="OBS" moodCode="EVN">
<templateId root="2.16.840.1.113883.10.20.6.2.12" />
<code code="121070" codeSystem="1.2.840.10008.2.16.4" codeSystemName="DCM" displayName="Findings">
</code>
<value xsi:type="ED">
<reference value="#121071">
</reference>
</value>
</observation>
</entry>
There can be any number of <entry> nodes, and they will all follow a similar pattern. The value under the root attribute on the templateId element contains a known UID that identifies this entry as the one I want. I need to get the reference value.
My thought is to find the correct templateID node, back out to the observation node, find <valuexsi:type="ED"> and then get the reference value. This seems overly complex, and I am wondering if there is another way to do this?
EDIT
The xml I receive can sometimes have xml nested under the same node name. In other words, <observation> may be located under another node named <observation>.
You have problems, because your document uses Namespaces, and your query is missing them.
First of all, you have to find xsi namespace declaration somewhere in your XML (probably in the most top element).
It will look like that:
xmlns:xsi="http://test.namespace"
The, take the namespace Uri and create XNamespace instance according to it's value:
var xsi = XNamespace.Get("http://test.namespace");
And use that xsi variable within your query:
var query = from o in xdoc.Root.Element("entries").Elements("entry").Elements("observation")
let tId = o.Element("templateId")
where tId != null && (string)tId.Attribute("root") == "2.16.840.1.113883.10.20.6.2.12"
let v = o.Element("value")
where v != null && (string)v.Attribute(xsi + "type") != null
let r = v.Element("reference")
where r != null
select (string)r.Attribute("value");
var result = query.FirstOrDefault();
I have tested it for following XML structure:
<root xmlns:xsi="http://test.namespace">
<entries>
<entry>
<observation classCode="OBS" moodCode="EVN">
<templateId root="2.16.840.1.113883.10.20.6.2.12" />
<code code="121070" codeSystem="1.2.840.10008.2.16.4" codeSystemName="DCM" displayName="Findings">
</code>
<value xsi:type="ED">
<reference value="#121071">
</reference>
</value>
</observation>
</entry>
</entries>
</root>
The query returns #121071 for it.
For your input XML you will probably have to change first line of query:
from o in xdoc.Root.Element("entries").Elements("entry").Elements("observation")
to match <observation> elements from your XML structure.
Would something along the lines of the following help?
XDocument xdoc = GetYourDocumentHere();
var obsvlookfor =
xdoc.Root.Descendants("observation")
.SingleOrDefault(el =>
el.Element("templateId")
.Attribute("root").Value == "root value to look for");
if (obsvlookfor != null)
{
var reference = obsvlookfor
.Element("value")
.Element("reference").Attribute("value").Value;
}
My thought is as follows:
Pull out all the observation elements in the document
Find the only one (or null) where the observation's templateId element has a root attribute you're looking for
If you find that observation element, pull out the value attribute against the reference element which is under the value element.
You might have to include the Namespace in your LINQ. To retrieve that you would do something like this:
XNamespace ns = xdoc.Root.GetDefaultNamespace();
Then in your linq:
var obsvlookfor = xdoc.Root.Descendants(ns + "observation")
I know I had some issues retrieving data once without this. Not saying its the issue just something to keep in mind particularly if your XML file is very in depth.
I'm building a database with meets, clubs, results and a lot more for swimmers in the Netherlands. Due to some changes in the data i am receiving i'm running into a problem with duplicate values in the XML files i am reading.
Here is part of an XML file that causes problems :
<LENEX version="3.0">
<MEETS>
<MEET name="Speedowedstrijd 2012 - 2013 deel 1">
<CLUB name="AZVD" type="CLUB" nation="NED" region="08" code="08-004">
<OFFICIALS>
<OFFICIAL nation="NED" gender="M" officialid="2329" lastname="xx">
<CONTACT email="xx" phone="xx" country="NL" />
</OFFICIAL>
</OFFICIALS>
</CLUB>
<CLUB name="A.Z.V.D." type="CLUB" nation="NED" region="8" code="08-004">
<ATHLETES>
<ATHLETE nation="NED" gender="M" athleteid="2358" license="xx" lastname="xx">
<RESULTS>
<RESULT eventid="1167" resultid="2359" swimtime="00:03:09.69">
<SPLITS>
<SPLIT distance="50" swimtime="00:00:40.71"/>
<SPLIT distance="100" swimtime="00:01:30.71"/>
</SPLITS>
</RESULT>
</RESULTS>
</ATHLETE>
</ATHLETES>
</CLUB>
</MEET>
</MEETS>
</LENEX>
Now the reading of the xml file is not a problem, using XDocument i get all the nodes, childs etc.
However, when i write the values to my database i get an keyconstraint error on the table Club_Meet. This table holds the link between the clubs table and the meet table and each conbination must be unique. As both clubs in the example above are pointing to the same club in my database (unique code = 08-004, i am trying to write the same values to the database twice, causing the error.
So waht i want to do is when i go through the xml file and find a club : check if this club was already found in this XML before and if so hang the childnodes under that first club-node.
The result of this action should be (internally) :
<LENEX version="3.0">
<MEETS>
<MEET name="Speedowedstrijd 2012 - 2013 deel 1">
<CLUB name="AZVD" type="CLUB" nation="NED" region="08" code="08-004">
<OFFICIALS>
<OFFICIAL nation="NED" gender="M" officialid="2329" lastname="xx">
<CONTACT email="xx" phone="xx" country="NL" />
</OFFICIAL>
</OFFICIALS>
<ATHLETES>
<ATHLETE nation="NED" gender="M" athleteid="2358" license="xx" lastname="xx">
<RESULTS>
<RESULT eventid="1167" resultid="2359" swimtime="00:03:09.69">
<SPLITS>
<SPLIT distance="50" swimtime="00:00:40.71"/>
<SPLIT distance="100" swimtime="00:01:30.71"/>
</SPLITS>
</RESULT>
</RESULTS>
</ATHLETE>
</ATHLETES>
</CLUB>
</MEET>
</MEETS>
</LENEX>
Note that the second club-node <CLUB name="A.Z.V.D." type="CLUB" nation="NED" region="8" code="08-004"> is removed completely, i dont need anything from that one.
How do i move the child nodes from one club to another and delete the empty club ?
Anyone that can point me in the right direction ?
(Hope this all makes some sense....)
OK so if you want to work strictly with the manipulation of your XML document, you can use the following extension method which I created.
public static class XmlExtensions
{
public static IEnumerable<XElement> CombineLikeElements(this IEnumerable<XElement> source, Func<XElement, object> groupSelector)
{
// used to record the newly combined elements
List<XElement> priElements = new List<XElement>();
// group the current xml nodes by the supplied groupSelector, and only
// select the groups that have more than 1 elements.
var groups = source.GroupBy(groupSelector).Where(grp => grp.Count() > 1);
foreach(var grp in groups)
{
// get the first (primary) child element and use it as
// element that all the other sibling elements get combined with.
var priElement = grp.First();
// get all the sibling elements which will be combined
// with the primary element. Skipping the primary element.
var sibElements = grp.Skip(1);
// add all the sibling element's child nodes to the primary
// element.
priElement.Add(sibElements.Select(node => node.Elements()));
// remove all of the sibling elements
sibElements.Remove();
// add the primary element to the return list
priElements.Add(priElement);
}
// return the primary elements incase we want to do some further
// combining of their descendents
return priElements;
}
}
You would use the extension method as follows:
XDocument xmlDoc = XDocument.Parse(xml);
xmlDoc
// Combine all of the duplicate CLUB nodes under each MEET node
.Descendants("MEET").Descendants("CLUB").CombineLikeElements(node => node.Attribute("code").Value);
And it would return the results that you requested.
I have the extension method returning a list of the XElements which everything was combined into in case you want to combine their child nodes. For example if after combining your identical CLUB elements, one or more of the CLUBs ends up having two or more ATHLETES or OFFICIALS nodes you can could combine those easily as well by doing the following:
xmlDoc
// Combine all of the duplicate CLUB nodes under each MEET node
.Descendants("MEET").Descendants("CLUB").CombineLikeElements(node => node.Attribute("code").Value)
// Combine all of the duplicate ALTHLETES or OFFICIALS nodes under the newly combined CLUB nodes
.Elements().CombineLikeElements(node => node.Name);