Why is XPathNodeIterator not finding desired path? - c#

I am having a problem with XPathNodeIterator grabbing the data from the given path. When debugging, pNav has all the values from the xml file. However iterator shows a count of 0. It never enters the while loop. Any help would be appreciated.
C#
XPathDocument pdoc = new XPathDocument("Courses.xml");
XPathNavigator pNav = pdoc.CreateNavigator();
XPathNodeIterator iterator = pNav.Select("/Courses/Course");
while (iterator.MoveNext())
{
XPathNodeIterator it = iterator.Current.Select("Name");
it.MoveNext();
string courseName = it.Current.Value;
it = iterator.Current.Select("Code");
it.MoveNext();
string courseCode = it.Current.Value;
Console.WriteLine("{0} {1}", courseName, courseCode);
}
XML:
<Courses xmlns="http://xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="Courses.xsd">
<Course>
<Code Undergrad="240"/>
<Name>Biology</Name>
<Instructor>
<Name>
<First>John</First>
<Last>Doe</Last>
</Name>
<Contact>
<Phone>898-989-8989</Phone>
</Contact>
</Instructor>
<Room>515</Room>
</Course>
</Courses>
I expect the output to be
Name = Biology, Code = 240

Because you have
xmlns="http://xml"
in your XML file you need to add a XmlNamespaceManager to allow the navigator to find the nodes. If you remove the xmlns="http://xml" from your XML then you won't need to use an XmlNamespaceManager.
Also the Select method returns a collection of nodes - you need to call SelectSingleNode to get the node you want. E.G.
XPathDocument pdoc = new XPathDocument("Courses.xml");
XPathNavigator pNav = pdoc.CreateNavigator();
var manager = new XmlNamespaceManager(pNav.NameTable);
manager.AddNamespace("cs", "http://xml");
XPathNodeIterator iterator = pNav.Select("/cs:Courses/cs:Course", manager);
while(iterator.MoveNext())
{
var nameNode = iterator.Current.SelectSingleNode("cs:Name", manager);
string courseName = nameNode.Value;
var codeNode = iterator.Current.SelectSingleNode("cs:Code", manager);
codeNode.MoveToFirstAttribute();
string courseCode = codeNode.Value;
Console.WriteLine("{0} {1}", courseName, courseCode);
}
When you get to the Code element, you need to move to the first attribute to get the value, otherwise Value property will return an empty string

It needs to pass namespace resolver to Select-method:
const string xml = #"
<Courses xmlns=""http://xml"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:schemaLocation=""Courses.xsd"">
<Course>
<Code Undergrad=""240""/>
<Name>Biology</Name>
<Instructor>
<Name>
<First>John</First>
<Last>Doe</Last>
</Name>
<Contact>
<Phone>898-989-8989</Phone>
</Contact>
</Instructor>
<Room>515</Room>
</Course>
<Course>
<Code Undergrad=""000""/>
<Name>Math</Name>
<Instructor>
<Name>
<First>John</First>
<Last>Doe</Last>
</Name>
<Contact>
<Phone>898-989-8989</Phone>
</Contact>
</Instructor>
<Room>515</Room>
</Course>
</Courses>";
using (var stream = new MemoryStream(Encoding.ASCII.GetBytes(xml)))
{
var pdoc = new XPathDocument(stream);
var pNav = pdoc.CreateNavigator();
var manager = new XmlNamespaceManager(pNav.NameTable);
manager.AddNamespace("cs", "http://xml");
var iterator = pNav.Select("/cs:Courses/cs:Course", manager);
foreach (XPathNavigator node in iterator)
{
var courseName = node.SelectSingleNode("cs:Name", manager)?.Value;
var courseCode = node.SelectSingleNode("cs:Code", manager)?.GetAttribute("Undergrad", string.Empty);
Console.WriteLine("{0} {1}", courseName, courseCode);
}
}

Related

Modify Child Node Value With C# XML Document Class

I am a total noob, so apologies, but I have searched everywhere and I'm still stuck.
I am writing a program which will modify 2 XML docs after the user has selected an initial XML document and renamed it. Once the user changes the filename of the first XML (let's call it XML #1), the other two XML docs must have the new filename inserted into specific node values within them (XML #2 and #3).
Each XML doc has multiple "Asset" nodes with exactly the same names, and I need to distinguish the node I want by using a unique UUID value that is present in XML Doc #1.
For both XML #2 and #3, the "Id" nodes contain this same unique UUID. I am parsing XML#1 to get this UUID, and assigning it to a variable called "cpluuid."
Then I am searching XML #2 and #3 for nodes with an "Id" = to "cpluuid", and attempting to modify the correct node that contains the file name to be inserted.
XML Doc #2 - aka var = packing (additional "Asset" nodes omitted)
<Asset>
<Id>urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c</Id>
<AnnotationText>blah</AnnotationText>
<Hash>5Yf4BV4GZ4qE9EjvtohZ8Rq8M2w=</Hash>
<Size>21881</Size>
<Type>text/xml</Type>
<OriginalFileName>CPL_IMF_JOT_Sample_143.xml</OriginalFileName>
<HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
</Asset>
I have had success with updating XML Doc #2 in this manner. The "OriginalFileName" node was successfully updated to the variable value "cpluuid" via this code:
XmlDocument xmlDoc = new XmlDocument();
XmlNamespaceManager ns = new XmlNamespaceManager(xmlDoc.NameTable);
ns.AddNamespace("cplns1", "http://www.smpte-ra.org/schemas/2067-3/2016");
ns.AddNamespace("cplns2", "http://www.w3.org/2001/XMLSchema-instance");
ns.AddNamespace("pklns", "http://www.smpte-ra.org/schemas/2067-2/2016/PKL");
ns.AddNamespace("assetns", "http://www.smpte-ra.org/schemas/429-9/2007/AM");
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(output);
string cpluuid = xmlDoc.SelectSingleNode("//cplns1:CompositionPlaylist/cplns1:Id", ns).InnerText;
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(packing);
XmlNodeList nodeList;
XmlNode root = xmlDoc.DocumentElement;
nodeList = root.SelectNodes("descendant::pklns:Asset[pklns:Id=\"" + cpluuid + "\"]", ns);
foreach (XmlNode Asset in nodeList)
{
Asset["OriginalFileName"].InnerText = (outfile);
}
xmlDoc.PreserveWhitespace = true;
xmlDoc.Save(packing);
However, I am having trouble with XML doc #3, because the node I need to modify ("Path") is nested further down in the tree:
XML Doc #3 - aka var = assetmap
<Asset>
<Id>urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c</Id>
<ChunkList>
<Chunk>
<Path>CPL_IMF_JOT_Sample.xml</Path>
<VolumeIndex>1</VolumeIndex>
<Offset>0</Offset>
<Length>21881</Length>
</Chunk>
</ChunkList>
</Asset>
So I tried using similar code, but cannot figure out how to drill down further since "Path" is not a sibling of "Asset." This code does not modify the "Path" value. I tried using different XPath syntax, but nothing I know works:
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(assetmap);
XmlNodeList nodeList2;
nodeList2 = root.SelectNodes("descendant::assetns:Asset[assetns:Id=\"" + cpluuid + "\"]", ns);
foreach (XmlNode Asset in nodeList2)
{
Asset["Path"].InnerText = (outfile);
}
xmlDoc.PreserveWhitespace = true;
xmlDoc.Save(assetmap);
I have tried various using System.Xml.Linq methods as well, but cannot get this to work.
UPDATE#1
Here is how I have been attempting to use XDocument. Seems like it should work, but always returns a Object Reference error.
var xml = XDocument.Load(assetmap);
var node = xml.Descendants("Asset").FirstOrDefault(asset => asset.Element("Id").Value == (cpluuid));
node.SetElementValue("Path", (outfile));
UPDATE #2
My second attempt using XDocument based on awesome feedback from #Juan M. Vergara. Unfortunately, while the code executes with no errors, the XML node does not update to the new value.
XDocument document = XDocument.Load(assetmap);
var assetElements = document.Elements("Asset");
foreach (var asset in assetElements)
{
var innerElements = asset.Elements("Id");
var matchingId = innerElements.FirstOrDefault(x => x.Value.Equals(cpluuid));
if (matchingId == null)
{
MessageBox.Show("UUID not found");
return;
}
var chunks = asset.Elements("ChunkList").First().Elements();
foreach (var chunk in chunks)
{
chunk.Elements("Path").First().SetValue(outfile);
}
}
document.Save(assetmap);
Here is a lager portion of the XML as well, in case there is an issue with namespaces or something else. The "Path" node that nneeds to be updated is the second one in the tree:
<?xml version="1.0" encoding="utf-8"?>
<AssetMap xmlns="http://www.smpte-ra.org/schemas/429-9/2007/AM">
<Id>urn:uuid:dc59ba55-adfd-4395-bdaa-de54202d014d</Id>
<Creator>Colorfront Transkoder 2017</Creator>
<VolumeCount>1</VolumeCount>
<IssueDate>2018-02-16T20:59:42-00:00</IssueDate>
<Issuer>Generic</Issuer>
<AssetList>
<Asset>
<Id>urn:uuid:296a656c-3610-4de1-9b08-2aa63245788d</Id>
<PackingList>true</PackingList>
<ChunkList>
<Chunk>
<Path>PKL_UUID.xml</Path>
<VolumeIndex>1</VolumeIndex>
<Offset>0</Offset>
<Length>3015</Length>
</Chunk>
</ChunkList>
</Asset>
<Asset>
<Id>urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c</Id>
<ChunkList>
<Chunk>
<Path>CPL_IMF_JOT_Sample.xml</Path>
<VolumeIndex>1</VolumeIndex>
<Offset>0</Offset>
<Length>21881</Length>
</Chunk>
</ChunkList>
</Asset>
</AssetList>
</AssetMap>
UPDATE #3
I'm trying the same method for XML#2 now, but it's not working. I assume because "OriginalFileName" is not the "First" element under "Asset." I'm trying different syntax combos, but nothing seems to work (sequence contains no element errors)
XDocument pkldoc = XDocument.Load(packing);
var pklns = pkldoc.Root.GetDefaultNamespace();
var packingList = pkldoc.Elements(pklns + "PackingList").First(); // this ignores the namespace
var pklassetList = packingList.Elements(pklns + "AssetList").First();
var pklassetElements = pklassetList.Elements(pklns + "Asset");
foreach (var pklasset in pklassetElements)
{
var innerElements = pklasset.Elements(pklns + "Id");
var matchingId = innerElements.FirstOrDefault(x => x.Value.Equals(cpluuid));
if (matchingId == null)
{
//MessageBox.Show("UUID not found");
continue;
}
var ofns = pklasset.Elements(pklns + "Asset").First().Elements();
foreach (var ofn in ofns)
{
ofn.Elements(pklns + "OriginalFileName").First().SetValue(outfile);
}
}
UPDATE #4
Here are the whole contents of XML #2. The structure is different than XML#3. I only want to update the "OriginalFileName" element in the very last "Asset" tree.
<?xml version="1.0" encoding="UTF-8"?>
<PackingList xmlns="http://www.smpte-ra.org/schemas/2067-2/2016/PKL">
<Id>urn:uuid:296a656c-3610-4de1-9b08-2aa63245788d</Id>
<AnnotationText>IMF_JOT_Sample</AnnotationText>
<IssueDate>2018-02-16T20:59:42-00:00</IssueDate>
<Issuer>Generic</Issuer>
<Creator>Generic</Creator>
<AssetList>
<Asset>
<Id>urn:uuid:744f36b7-fc7e-4179-8b75-c71c18f98156</Id>
<AnnotationText>Video_744f36b7-fc7e-4179-8b75-c71c18f98156.mxf</AnnotationText>
<Hash>8HhnKnLn+Lp/Ik9i94Ml4SXAxH4=</Hash>
<Size>14568486</Size>
<Type>application/mxf</Type>
<OriginalFileName>Video_744f36b7-fc7e-4179-8b75-c71c18f98156.mxf</OriginalFileName>
<HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
</Asset>
<Asset>
<Id>urn:uuid:bf5438ea-ba58-4ae0-a64a-5d23cee2ebb3</Id>
<AnnotationText>Audio_bf5438ea-ba58-4ae0-a64a-5d23cee2ebb3.mxf</AnnotationText>
<Hash>Wg4aEAE5Ji9e14ZyGkvfUUjBwCw=</Hash>
<Size>4341294</Size>
<Type>application/mxf</Type>
<OriginalFileName>Audio_bf5438ea-ba58-4ae0-a64a-5d23cee2ebb3.mxf</OriginalFileName>
<HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
</Asset>
<Asset>
<Id>urn:uuid:dd5a88d2-ccec-4b22-8584-fda51945c3ea</Id>
<AnnotationText>Audio_dd5a88d2-ccec-4b22-8584-fda51945c3ea.mxf</AnnotationText>
<Hash>OwjRFnWZCdKHSZ+3PBXroDhMMlY=</Hash>
<Size>1458414</Size>
<Type>application/mxf</Type>
<OriginalFileName>Audio_dd5a88d2-ccec-4b22-8584-fda51945c3ea.mxf</OriginalFileName>
<HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
</Asset>
<Asset>
<Id>urn:uuid:9e11458a-71fb-4702-8609-55d2308dcc64</Id>
<AnnotationText>Sub_9e11458a-71fb-4702-8609-55d2308dcc64.mxf</AnnotationText>
<Hash>48KyxgwCJVXIdgAGfaNApheQN5M=</Hash>
<Size>34509</Size>
<Type>application/mxf</Type>
<OriginalFileName>Sub_9e11458a-71fb-4702-8609-55d2308dcc64.mxf</OriginalFileName>
<HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
</Asset>
<Asset>
<Id>urn:uuid:3f57f474-4c81-438e-a67d-1b08fa09a10d</Id>
<AnnotationText>IMF_JOT_Sample</AnnotationText>
<Hash>q8TiPkg/3devlN3LXnBhrgkZ968=</Hash>
<Size>713</Size>
<Type>text/xml</Type>
<OriginalFileName>OPL_3f57f474-4c81-438e-a67d-1b08fa09a10d.xml</OriginalFileName>
<HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
</Asset>
<Asset>
<Id>urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c</Id>
<AnnotationText>CPL_IMF_JOT_Sample.xml</AnnotationText>
<Hash>5Yf4BV4GZ4qE9EjvtohZ8Rq8M2w=</Hash>
<Size>21881</Size>
<Type>text/xml</Type>
<OriginalFileName>CPL_IMF_JOT_Sample.xml</OriginalFileName>
<HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
</Asset>
UPDATE #5
Thanks again Juan for your help. Here is the working solution for XML Doc #2. I know it's not very pretty, but it works.
At some point I'd like to go back and fully digest your last example and combine / cleanup my code the way you have. THANKS!
XDocument pkldoc = XDocument.Load(packing);
var pklns = pkldoc.Root.GetDefaultNamespace();
var pklassetElements = pkldoc.Descendants(pklns + "Asset");
foreach (var pklasset in pklassetElements)
{
var idElement = pklasset.Descendants(pklns + "Id").First();
if (!idElement.Value.Equals(cpluuid))
continue;
SetNewValue(pklasset, pklns + "OriginalFileName", outfile);
}
void SetNewValue(XElement currentElement, XName elementName, string newValue)
{
var matchingElements = currentElement.Descendants(elementName);
if (matchingElements.Any())
{
foreach (var element in matchingElements)
element.SetValue(newValue);
}
}
pkldoc.Save(packing);
I preferred to write a different answer, due to the new conditions.
Given you have those 2 sample xmls, with different structures but with common nodes, I have given another try using Descendants.
You basically need to find the Asset elements, then check whether the Id matches, and if so, update the Path or OriginalFileName element, try this code:
private void Run()
{
XDocument doc1 = XDocument.Load("xml1.xml");
XDocument doc2 = XDocument.Load("xml2.xml");
var id = #"urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c";
ReplaceId(doc1, id, "new path");
ReplaceId(doc2, id, "new path");
doc1.Save("xml1_new.xml");
doc2.Save("xml2_new.xml");
Console.WriteLine("Enter to exit...");
Console.ReadLine();
}
private void ReplaceId(XDocument doc, string id, string newValue)
{
var ns = doc.Root.GetDefaultNamespace();
var assetElements = doc.Descendants(ns + "Asset");
foreach (var element in assetElements)
{
var idElement = element.Descendants(ns + "Id").First();
if (!idElement.Value.Equals(id))
continue;
// for xml model #1
SetNewValue(element, ns + "Path", newValue);
// for xml model #2
SetNewValue(element, ns + "OriginalFileName", newValue);
}
}
private void SetNewValue(XElement currentElement, XName elementName, string newValue)
{
var matchingElements = currentElement.Descendants(elementName);
if (matchingElements.Any())
{
foreach (var element in matchingElements)
element.SetValue(newValue);
}
}
Let me now how it goes with this one.
UPDATE
A modification to ReplaceId to know if a replacement has been done:
private bool ReplaceId(XDocument doc, string id, string newValue)
{
var ns = doc.Root.GetDefaultNamespace();
var assetElements = doc.Descendants(ns + "Asset");
var result = false;
foreach (var element in assetElements)
{
var idElement = element.Descendants(ns + "Id").First();
if (!idElement.Value.Equals(id))
continue;
// for xml model #1
SetNewValue(element, ns + "Path", newValue);
// for xml model #2
SetNewValue(element, ns + "OriginalFileName", newValue);
result = true;
}
return result;
}
I just gave it a try and put together some code in a console application example, bear in mind that I do not control all errors in al cases (like element not found...), but I think that it can give you an idea of how you can use System.Xml.Linq.
Hope it helps.
using System;
using System.Linq;
using System.Xml.Linq;
namespace ParsingXML
{
class Program
{
const string _xml =
#"<Asset>
<Id>urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c</Id>
<ChunkList>
<Chunk>
<Path>CPL_IMF_JOT_Sample.xml</Path>
<VolumeIndex>1</VolumeIndex>
<Offset>0</Offset>
<Length>21881</Length>
</Chunk>
</ChunkList>
</Asset>";
static void Main(string[] args)
{
ReplacePath(
#"urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c",
#"c:\path\to\somefilename.xml"
);
Console.WriteLine("Enter to exit...");
Console.ReadLine();
}
static void ReplacePath(string id, string pathToSet)
{
XDocument document = XDocument.Parse(_xml);
var assetElements = document.Elements("Asset");
foreach (var asset in assetElements)
{
var innerElements = asset.Elements("Id");
var matchingId = innerElements.FirstOrDefault(e => e.Value.Equals(id));
if (matchingId == null)
{
Console.WriteLine("id not found");
return;
}
var chunks = asset.Elements("ChunkList").First().Elements();
foreach (var chunk in chunks)
{
chunk.Elements("Path").First().SetValue(pathToSet);
}
}
var xml = document.ToString();
Console.WriteLine(xml);
Console.WriteLine("Enter to exit...");
Console.ReadLine();
}
}
}
And this is the output I get:
<Asset>
<Id>urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c</Id>
<ChunkList>
<Chunk>
<Path>c:\path\to\somefilename.xml</Path>
<VolumeIndex>1</VolumeIndex>
<Offset>0</Offset>
<Length>21881</Length>
</Chunk>
</ChunkList>
</Asset>
Enter to exit...
Update
Problem is that there are more than one Asset, and as the first one is not matching the first foreach gives a message and stops execution in the return, just replace the return statement by a continue instruction, so the execution does not stop.
In the other hand, you may have to include tha namespace, this is the updated code of my initial ReplacePath function:
static void ReplacePath(string targetFile, string id, string outfile)
{
//XDocument document = XDocument.Parse(_xml);
XDocument document = XDocument.Load(targetFile);
// get root namespace to use with rest of element names
var ns = document.Root.GetDefaultNamespace();
var assetMap = document.Elements(ns + "AssetMap").First(); // this ignores the namespace
var assetList = assetMap.Elements(ns + "AssetList").First();
var assetElements = assetList.Elements(ns + "Asset");
foreach (var asset in assetElements)
{
var innerElements = asset.Elements(ns + "Id");
var matchingId = innerElements.FirstOrDefault(e => e.Value.Equals(id));
if (matchingId == null)
{
continue;
}
var chunks = asset.Elements(ns + "ChunkList").First().Elements();
foreach (var chunk in chunks)
{
var chunkElement = chunk.Elements(ns + "Path").First();
chunkElement.SetValue(outfile);
}
}
var xml = document.ToString();
Console.WriteLine(xml);
Console.WriteLine("writing to file");
document.Save(targetFile);
Console.WriteLine("Enter to exit...");
Console.ReadLine();
}
I hope this time you got the right answer :).

Traversing XML document using LINQ

I'm trying to read in the address position values of a specific product (e.g. Payslips) in the below XML
<INI>
<ReportTemplate>report_template_land.pdf</ReportTemplate>
<ReportAccountID>Reports</ReportAccountID>
<!--Table for sending the documents to different channels-->
<ChannelDeliveryTable>ChannelDeliveryTable.csv</ChannelDeliveryTable>
<Documents>
<Payslip>
<Address>
<distanceInPixelsFromLeft>76</distanceInPixelsFromLeft>
<distanceInPixelsFromBottom>580</distanceInPixelsFromBottom>
<width>255</width>
<height>125</height>
</Address>
</Payslip>
<Invoice>
<Address>
<distanceInPixelsFromLeft>65</distanceInPixelsFromLeft>
<distanceInPixelsFromBottom>580</distanceInPixelsFromBottom>
<width>255</width>
<height>125</height>
</Address>
</Invoice>
</Documents>
</INI>
I had couple of attempts which all failed. The below code shows my last attempt. Could you please help. Thanks in advance.
float distanceInPixelsFromLeftAddr;
float distanceInPixelsFromBottomAddr;
float widthAddr;
float heightAddr;
try
{
//var addrPos = from xml in XmlDoc.Elements("Payslip").Descendants("Address")
var addrPos = from xml in XmlDoc.Descendants("Payslip").Descendants("Address")
select new
{
distanceInPixelsFromLeftAddr = xml.Element("distanceInPixelsFromLeft").Value,
distanceInPixelsFromBottomAddr = xml.Element("distanceInPixelsFromBottom").Value,
widthAddr = xml.Element("width").Value,
heightAddr = xml.Element("height").Value
};
foreach (var node in addrPos)
{
distanceInPixelsFromLeftAddr = float.Parse(node.distanceInPixelsFromLeftAddr);
distanceInPixelsFromBottomAddr = float.Parse(node.distanceInPixelsFromBottomAddr);
widthAddr = float.Parse(node.widthAddr);
heightAddr = float.Parse(node.heightAddr);
}
}
Given no default namespace is involved, the following query works just fine against the XML posted in question :
var addrPos = from xml in XmlDoc.Descendants("Payslip").Elements("Address")
select new
{
distanceInPixelsFromLeftAddr = (string)xml.Element("distanceInPixelsFromLeft"),
distanceInPixelsFromBottomAddr = (string)xml.Element("distanceInPixelsFromBottom"),
widthAddr = (float)xml.Element("width"),
heightAddr = (float)xml.Element("height")
};
Notice how you can cast XElement directly to the appropriate type such as string or float.
See the demo below or see it live in dotnetfiddle :
var raw = #"<INI>
<ReportTemplate>report_template_land.pdf</ReportTemplate>
<ReportAccountID>Reports</ReportAccountID>
<!--Table for sending the documents to different channels-->
<ChannelDeliveryTable>ChannelDeliveryTable.csv</ChannelDeliveryTable>
<Documents>
<Payslip>
<Address>
<distanceInPixelsFromLeft>76</distanceInPixelsFromLeft>
<distanceInPixelsFromBottom>580</distanceInPixelsFromBottom>
<width>255</width>
<height>125</height>
</Address>
</Payslip>
<Invoice>
<Address>
<distanceInPixelsFromLeft>65</distanceInPixelsFromLeft>
<distanceInPixelsFromBottom>580</distanceInPixelsFromBottom>
<width>255</width>
<height>125</height>
</Address>
</Invoice>
</Documents>
</INI>
";
var XmlDoc = XDocument.Parse(raw);
var addrPos = from xml in XmlDoc.Descendants("Payslip").Elements("Address")
select new
{
distanceInPixelsFromLeftAddr = (string)xml.Element("distanceInPixelsFromLeft"),
distanceInPixelsFromBottomAddr = (string)xml.Element("distanceInPixelsFromBottom"),
widthAddr = (float)xml.Element("width"),
heightAddr = (float)xml.Element("height")
};
foreach (var node in addrPos)
{
Console.WriteLine(node);
}
Output :
{ distanceInPixelsFromLeftAddr = 76, distanceInPixelsFromBottomAddr = 580, widthAddr = 255, heightAddr = 125 }

XmlDocument.SelectSingleNode

I have a soap xml message and need to fetch a single node value from given soap xml
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://tews6/wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.xmlsoap.org/soap/envelope/ http://schemas.xmlsoap.org/soap/envelope/" >
<soapenv:Body>
<testResult>
<Status version="6.0" >
<NNO>277982b4-2917a65f-13ffb8c0-b09751f</NNO>
</Status>
<ProfileTab>
<Email>abc#gmail.com</Email>
<Name>abc</Name>
</Profile>
</testResult></soapenv:Body></soapenv:Envelope>
I need to fetch the value of Email node. I used the below code
rootNode = "soapenv:Envelope/soapenv:Body/ProfileTab/Email";
var nsmgr = new XmlNamespaceManager(document.NameTable);
nsmgr.AddNamespace("xsl", "http://www.w3.org/1999/XSL/Transform");
nsmgr.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
node = document.SelectSingleNode(rootNode,nsmgr);
It is returning the null.
You can use the following.
var rootNode = "soapenv:Envelope/soapenv:Body/tews6:testResult/tews6:ProfileTab/tews6:Email";
var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("tews6", "http://tews6/wsdl");
nsmgr.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
var node = doc.SelectSingleNode(rootNode, nsmgr);
Try this:
string xml="xml";
XDocument doc = XDocument.Parse(xml);
XNamespace bodyNameSpace ="http://schemas.xmlsoap.org/soap/envelope/";
var bodyXml = from _e in doc.Descendants(bodyNameSpace + "Body")
select _e;
if (bodyXml.Elements().Count() == 0)
{
return;
}
var email = from _e in bodyXml.First()Descendants("Email")
select _e;
if(email.Count()==1)
{
string emailAddress=email.First().Value;
}

Reading value from XML

I am new to C# asp.net coding so I am facing a bit problem.
This is my xml file. I want to retrieve " < DOB > " values of each employee and want to store them in a list, say "emps_dob". Please help me with this. Thank u
<?xml version="1.0" encoding="utf-8"?>
<employees>
<employee>
<name> Vin </name>
<DOB> 07/10 </DOB>
<emailID> vinay#abc.com</emailID>
</employee>
<employee>
<name> ben </name>
<DOB> 08/11 </DOB>
<emailID> ben#abc.com</emailID>
</employee>
<employee>
<name> tin </name>
<DOB> 09/12 </DOB>
<emailID> tin#abc.com</emailID>
</employee>
You can use linq as per answer given in this post
var doc = XDocument.Load("yourfilepath")
var dobs= doc.Root.Elements().Select( x => x.Element("DOB") );
OR
using System;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main( string[] args )
{
XDocument doc = XDocument.Load( "XMLFile1.xml" );
List<string> emps_dob=new List<string>();
var dobs= doc.Descendants( "DOB" );
foreach ( var item in dobs)
{
emps_dob.Add(item.Value);
}
}
}
}
XmlDocument xml = new XmlDocument();
xml.LoadXml(myXmlString);
XmlNodeList xnList = xml.SelectNodes("/employees/employee");
foreach (XmlNode xn in xnList)
{
string name= xn["name"].InnerText;
string DOB= xn["name"].InnerText;
Console.WriteLine("Name: {0} {1}", name, DOB);
}
using System.Xml;
List<string> dob = new List<string>();
XmlDocument doc = new XmlDocument();
doc.Load("abc.xml");
XmlNode root = doc.DocumentElement;
foreach (XmlNode node1 in root.ChildNodes)
{
foreach (XmlNode node2 in node1.ChildNodes)
{
if (node2.Name.ToString() == "DOB")
dob.Add(node2.InnerText.ToString());
}
}

Add attributes using XAttribute

I have a root XML which is like this:
<Root xmlns="http://schemas.datacontract.org/2004/07/" xmlns:t="http://www.w3.org/2001/XMLSchema-instance">
<Element1>somevalue</Element1>
<Data/>
<Root>
I need to add few customer related informaiton like (Name, Age etc) under Data Element Node. So, the result I expect is follows:
<Root xmlns="http://schemas.datacontract.org/2004/07/" xmlns:t="http://www.w3.org/2001/XMLSchema-instance">
<Element1>somevalue</Element1>
<Data>
<Name t:type="xs:string" xmlns="" xmlns:s="http://www.w3.org/2001/XMLSchema">Elvis</Name>
<Address t:type="xs:string" xmlns="" xmlns:s="http://www.w3.org/2001/XMLSchema">Some address</Address>
</Data>
<Root>
How can I achieve this? I am using C#, .net 4.0.
I was trying out the below code, but didn't get the result I was expecting:
string strTemplate = "<Root xmlns='http://schemas.datacontract.org/2004/07/' xmlns:t='http://www.w3.org/2001/XMLSchema-instance'><Element1>somevalue</Element1><Data/></Root>";
XDocument doc = XDocument.Parse(strTemplate);
XElement rootElement = doc.Root;
XElement dataElement = null;
foreach (XElement descendant in rootElement.Descendants())
{
if (descendant.Name.LocalName == "Data")
{
dataElement = descendant;
break;
}
}
string cusData = "<CustData><Name>Elvis</Name><Address>XYZ</Address></CustData>";
XElement customerElements = XElement.Parse(cusData);
if (dataElement != null)
{
foreach (XElement custAttributes in customerElements.Descendants())
{
XNamespace t = "http://www.w3.org/2001/XMLSchema-instance";
var attribute = new XAttribute(t + "type", "xs:string");
var attribute1 = new XAttribute(XNamespace.Xmlns + "s", "http://www.w3.org/2001/XMLSchema");
XElement element = new XElement(custAttributes.Name, attribute, attribute1);
element.Value = custAttributes.Value;
dataElement.Add(element);
}
}
string strPayload = doc.ToString();
When I execute the code I get the following:
<Data xmlns="http://schemas.datacontract.org/2004/07/">
<Name t:type="xs:string" xmlns:t="http://www.w3.org/2001/XMLSchema-instance" xmlns="">Elvis</Name>
<Address t:type="xs:string" xmlns:t="http://www.w3.org/2001/XMLSchema-instance" xmlns="">XYZ</Address>
</Data>
Any help appreciated!
Thanks,
M

Categories

Resources