Using XmlTextReader to Loop though XML attributes that have the same name - c#

I am doing some practice code with the XmlTextReader. I have written some very basic XML as shown here:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<task name="mixed_sprite_task_test">
<sprite>
<type>animatedSprite</type>
<id>0</id>
<name>animatedSprite</name>
<fileName>iyezoz</fileName>
<startingPositionX>200</startingPositionX>
<startingPositionY>200</startingPositionY>
<sheetSizeX>12</sheetSizeX>
<sheetSizeY>35</sheetSizeY>
<startingFrameX>0</startingFrameX>
<startingFrameY>0</startingFrameY>
<startingState>standing</startingState>
<movementSpeed>15</movementSpeed>
<frameDelay>0.055</frameDelay>
</sprite>
<sprite>
<type>staticSprite</type>
<id>0</id>
<name>staticSprite</name>
<fileName>Super_Mario_63</fileName>
<startingPositionX>0</startingPositionX>
<startingPositionY>0</startingPositionY>
</sprite>
<sprite>
<type>simpleSprite</type>
<id>0</id>
<name>simpleSprite</name>
<fileName>imgres</fileName>
<startingPositionX>100</startingPositionX>
<startingPositionY>100</startingPositionY>
<movementSpeed>15</movementSpeed>
</sprite>
</task>
This file shows that I have a task. In the task I have 3 sprites.
In my code I want to loop through each sprite and collect the information.
I can get the data from the first sprite with no issue. Is there a certain way to loop through an xml with attributes of the same name?
Thank-you!

I prefer Linq2Xml.
var xDoc = XDocument.Parse(xmlstring); //or XDocument.Load(filename);
var sprites = xDoc.Descendants("sprite")
.Select(s=>s.Elements()
.ToDictionary(e=>e.Name.LocalName,e=>(string)e))
.ToList();
You can use it as
var type = sprites[0]["type"];
or can take a safe action
string delay;
if (sprites[1].TryGetValue("frameDelay", out delay))
{
Console.WriteLine(delay);
}

You can select all the nodes named "sprite"
var myXml = new XmlDocument();
myXml.Load(myDocument);
XmlNode rootElement = myXml.DocumentElement;
foreach (XmlNode item in rootElement.SelectNodes(#"/task/sprite"))
{
// do stuff with node
}

Related

How can I parse the following xml in c#

I need some help on how to get values of English and studentId from this XML. Maybe it needs some special parsing I can't figure out at the moment.
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<processResponse xmlns="http://tempuri.org/">
<processResult>
<xml version="1.0" encoding="UTF-8"?>
<returnedResponse>
<English>94</English>
<Remarks>Excellent</Remarks>
<studentId>tst005</studentId>
<Department>6</Department>
</returnedResponse>
</processResult>
</processResponse>
</soap:Body>
</soap:Envelope>
I have tried the following code:
XmlDocument xdr = new XmlDocument();
xdr.LoadXml(thexml);
XmlNodeList nodelist = xdr.SelectNodes("//processResponse/processResult/returnedResponse");
foreach (XmlNode node in nodelist)
{
string eng = node.SelectSingleNode("English").InnerText;
Response.Write("eng");
}
Do you get an error with invalid XML or does your SelectNodes not find anything?
How about this path
XmlNodeList nodelist = xdr.SelectNodes("/*/*/*/processResponse/processResult/returnedResponse");
You can try with SelectSingleNode, just like this.
var English= xdr.DocumentElement.SelectSingleNode("English").InnerText;
var studentId = xdr.DocumentElement.SelectSingleNode("studentId").InnerText;
1) XML ist invalid at line 6 -> because it does not have a closing tag. If you cannot edit the Service and strip the xml header or encode the resulting xml, you have to strip the line 6 in you consumercode.
2) You have to define the Namespaces!
Btw: I highly recommend using (Linq2XML) XDocument. Your code could look like this:
XDocument xdr = XDocument.Parse(...);
var nodes = xdr.XPathSelectElements("//*[name()='returnedResponse']");
Try this
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlString);
XmlNodeList nList = doc.GetElementsByTagName("returnedResponse");
foreach (XmlNode node in nList)
{
XElement xelement = XElement.Parse(node.OuterXml);
var Descendents = xelement.Descendants();
foreach (var item in Descendents)
{
//you'll get each of your descendents here
}
}

Search through XML and grab another Node

<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Message>
<MessageID>1</MessageID>
<Product>
<SKU>33333-01</SKU>
</Product>
</Message>
</Envelope>
I've tried googling but whether I'm just not providing the correct search criteria I don't know.
I want to be able to search the XML file based on the MessageID and then grab the SKU.
I then want to search another XML file based on the SKU and remove that message completely.
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Message>
<MessageID>1</MessageID>
<Inventory>
<SKU>33333-01</SKU>
<Quantity>1</Quantity>
</Inventory>
</Message>
<Message>
<MessageID>2</MessageID>
<Inventory>
<SKU>22222-01</SKU>
<Quantity>1</Quantity>
</Inventory>
</Message>
</Envelope>
Meaning the XML above becomes:
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Message>
<MessageID>2</MessageID>
<Inventory>
<SKU>22222-01</SKU>
<Quantity>1</Quantity>
</Inventory>
</Message>
</Envelope>
To confirm I cannot confirm that the MessageID will be the same over different XML files.
Thanks in advance for any help.
My questions:
How do I search through XML files?
How do I then grab another Nodes details
Can I remove a complete from an XML file based on a search?
You can use XmlDocument to load your XML document. Then, you can use XPath for searching any nodes.
XmlDocument document = new XmlDocument();
document.Load("C:\fileOnTheDisk.xml");
// or
document.LoadXml("<a>someXmlString</a>");
// Returns single element or null if not found
var singleNode = document.SelectSingleNode("Envelope/Message[MessageID = '1']");
// Returns a NodeList
var nodesList = document.SelectNodes("Envelope/Message[MessageID = '1']");
Read more about XPath at w3schools.com.
Here is a good XPath Tester.
For example, you can use the following XPath to find nodes in your document by ID:
XmlDocument document = new XmlDocument();
document.Load("C:\doc.xml");
var node = document.SelectSingleNode("Envelope/Message[MessageID = '1']");
var sku = node.SelectSingleNode("Inventory/SKU").InnerText;
Console.WriteLine("{0} node has SKU = {1}", 1, sku);
Or you can output all SKUs:
foreach (XmlNode node in document.SelectNodes("Envelope/Message"))
{
Console.WriteLine("{0} node has SKU = {1}",
node.SelectSingleNode("MessageID").InnerText,
node.SelectSingleNode("Inventory/SKU").InnerText);
}
It will produce:
1 node has SKU = 33333-01
2 node has SKU = 22222-01
Note that there are possible NullReferenceExceptions if nodes are not present.
You can simply remove it using RemoveChild() method of its parent.
XmlDocument document = new XmlDocument();
document.Load("C:\doc.xml");
var node = document.SelectSingleNode("Envelope/Message[MessageID = '1']");
node.ParentNode.RemoveChild(node);
document.Save("C:\docNew.xml"); // will be without Message 1
You can use Linq to XML to do this:
var doc= XDocument.Load("input.xml");//path of your xml file in which you want to search based on message id.
var searchNode= doc.Descendants("MessageID").FirstOrDefault(d => d.Value == "1");// It will search message node where its value is 1 and get first of it
if(searchNode!=null)
{
var SKU=searchNode.Parent.Descendants("SKU").FirstOrDefault();
if(SKU!=null)
{
var searchDoc=XDocument.Load("search.xml");//path of xml file where you want to search based on SKU value.
var nodes =searchDoc.Descendants("SKU").Where(d=>d.Value==SKU.Value).Select(d=>d.Parent.Parent).ToList();
nodes.ForEach(node=>node.Remove());
searchDoc.Save("output.xml");//path of output file
}
}
I'd recommend you did this using LINQ to XML - it's much nicer to work with than the old XmlDocument API.
For all the examples, you can parse your XML string xml to an XDocument like so:
var doc = XDocument.Parse(xml);
1. How do I search through XML files?
You can get the SKU for a specific message ID by querying your document:
var sku = (string)doc.Descendants("Message")
.Where(e => (int)e.Element("MessageID") == 1)
.SelectMany(e => e.Descendants("SKU"))
.Single();
2. How do I then grab another Nodes details?
You can get the Message element with a specified SKU using a another query:
var message = doc.Descendants("SKU")
.Where(sku => (string)sku == "33333-01")
.SelectMany(e => e.Ancestors("Message"))
.Single();
3. Can I remove a complete element from an XML file based on a search?
Using your result from step 2, you can simple call Remove:
message.Remove();
Alternatively, you can combine the query from step 2 and simply execute a command to remove any messages that have a specific SKU:
doc.Descendants("SKU")
.Where(sku => (string)sku == "33333-01")
.SelectMany(e => e.Ancestors("Message"))
.Remove();
I tried to answer all your questions:
using System.Xml.XPath;
using System.Xml.Linq;
XDocument xdoc1 = XDocument.Load("xml1.xml");
XDocument xdoc2 = XDocument.Load("xml2.xml");
string sku = String.Empty;
string searchedID = "2";
//1.searching through an xml file based on path
foreach (XElement message in xdoc1.XPathSelectElements("Envelope/Message"))
{
if (message.Element("MessageID").Value.Equals(searchedID))
{
//2.grabbing another node's details
sku = message.XPathSelectElement("Inventory/SKU").Value;
}
}
foreach (XElement message in xdoc2.XPathSelectElements("Envelope/Message"))
{
if (message.XPathSelectElement("Inventory/SKU") != null && message.XPathSelectElement("Inventory/SKU").Value.Equals(sku))
{
//removing a node
message.Remove();
}
}
xdoc2.Save("xml2_del.xml");
}

Very simple XML reading in C#.NET

I want to read the following XML file:
Words.xml:
<?xml version="1.0" encoding="utf-8" ?>
<words>
<word>Bat</word>
<word>Dog</word>
<word>Car</word>
</words>
..using XDocument. I keep getting a "Non white space characters cannot be added to content" error with this code:
XDocument doc = new XDocument("words.xml");
foreach (XElement element in doc.Descendants("word"))
{
Console.WriteLine(element.Value);
}
You need to load the document like this:
XDocument doc = XDocument.Load("words.xml");
The reason your original code fails is you are using XDocument (Object[]) constructor that normally expects list of XElement objects like:
var doc = new XDocument(new XElement("Root"));

xml error:System.Xml.XPath.XPathException: Expression must evaluate to a node-set

I am trying to find a node in my xml file but getting the error ( see title)?
// instantiate XmlDocument and load XML from file
XmlDocument doc = new XmlDocument();
doc.Load(#"C:\temp\test2.xml");
var node = doc.SelectSingleNode("/Offers/Offer/ID=[text()='1']");
var test = node;
xml
<?xml version="1.0" encoding="utf-8"?>
<Offers>
<Offer>
<Model>AAAA</Model>
<ID>1</ID>
<Name>First offer</Name>
</Offer>
<Offer>
<Model>BBBB</Model>
<ID>2</ID>
<Name>Second offer</Name>
</Offer>
</Offers>
Remove the = after ID:
var node = doc.SelectSingleNode("/Offers/Offer/ID=[text()='1']");
becomes:
var node = doc.SelectSingleNode("/Offers/Offer/ID[text()='1']");

c# Parsing XML issue

I am using this code to try and read this XML below, but it is not read anything in the foreach loop and skipping that. Thanks.
C#
XElement _xml;
_xml = XElement.Parse(PostResult);
foreach (XElement value in _xml.Elements("ServiceDelivery")
.Element("StopMonitoringDelivery")
.Elements("MonitoredStopVisit"))
{
StopFeed _item = new StopFeed();
_item.A= value.Element("PublishedLineName").Value;
_item.B = value.Element("DirectionName").Value;
listBox1.Items.Add(_item);
}
XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Siri version="1.0" xmlns="http://www.siri.org.uk/">
<ServiceDelivery>
<ResponseTimestamp>2011-10-04T11:45:36.415+01:00</ResponseTimestamp>
<ResponseMessageIdentifier>1d8bc237-2df6-43e6-af56-c750d3089eb6</ResponseMessageIdentifier>
<StopMonitoringDelivery version="1.0">
<ResponseTimestamp>2011-10-04T11:45:36.415+01:00</ResponseTimestamp>
<RequestMessageRef>1</RequestMessageRef>
<MonitoredStopVisit>
<RecordedAtTime>2011-10-04T11:45:36.412+01:00</RecordedAtTime>
<MonitoringRef>020035057</MonitoringRef>
<MonitoredVehicleJourney>
<FramedVehicleJourneyRef>
<DataFrameRef>-</DataFrameRef>
<DatedVehicleJourneyRef>-</DatedVehicleJourneyRef>
</FramedVehicleJourneyRef>
<VehicleMode>bus</VehicleMode>
<PublishedLineName>28</PublishedLineName>
<DirectionName>blahblah</DirectionName>
<OperatorRef>153</OperatorRef>
<MonitoredCall>
<AimedDepartureTime>2011-10- 04T11:48:00.000+01:00</AimedDepartureTime>
</MonitoredCall>
</MonitoredVehicleJourney>
</MonitoredStopVisit>
</StopMonitoringDelivery>
</ServiceDelivery>
</Siri>
I think you forgot the namespace. Have a look at XNamespace
XNamespace xn= "http://www.siri.org.uk/";
foreach (XElement value in _xml.Elements(xn+ "ServiceDelivery")
.Element(xn+ "StopMonitoringDelivery")
.Elements(xn+ "MonitoredStopVisit"))
{
....
}
The root element is Siri.
Either use:
XElement value in _xml.Root.Elements("ServiceDelivery")
.Element("StopMonitoringDelivery")
.Elements("MonitoredStopVisit"))
or
XElement value in _xml.Element("Siri")
.Elements("ServiceDelivery")
.Element("StopMonitoringDelivery")
.Elements("MonitoredStopVisit"))
/B
try _xml.Elements("//ServiceDelivery") or _xml.Elements("Siri/ServiceDelivery"), you forgot that your ServiceDelivery node is child of Siri node.
Or maybe the XML namespace is the problem, check this page and see how XmlNamespaceManager can be used.

Categories