I am working on some code in C# where I want the output as the text present at some xpath from some xml-file. Now as the xml file keeps changing and so do the namespaces, I don't want to hardcode the namespaces in the code.
XmlDocument xml = new XmlDocument();
xml.Load(file);
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xml.NameTable);
Now as the namespace keeps changing I am thinking of reading the xml and using some string operations to get the namspace and uri string:
string s = System.IO.File.ReadAllText(file);
string[] s1 = new string[1];
s1[0]="xmlns:";
string[] s3 = s.Split(s1, System.StringSplitOptions.None);
foreach (string k in s3)
{
nsMgr.AddNamespace( k.Substring( 0 , k.IndexOf('=') - 1) , need help for this )
}
Some sample values of k after the split operation are:
"xs=\"http://www.w3.org/2001/XMLSchema\" "
"location=\"urn:x-ABC:content:location:mastering:1\" "
"entity=\"urn:x-ABC:content:identified-entities:mastering:1\" "
I need help on second parameter of nsMgr.AddNamespace(). Also if there is a cleaner way of adding namespaces without hardcoding.
EDIT:- Clarifying what i am doing here. I am trying to write a winform through which onc can get output as text present at some xpath in some xml. So winform takes 2 inputs .One is xml file location and other is xpath . The output should be the text at that xpath location.
For example
<a>
<b>abc
</b>
</a>
if user searches for //b or a/b then output should be abc.
The code works fine when there are default namespaces , but when the xml has namespaces defined i need to include them in the code. As i want to make the code generic , so i cannot hardcode the namespaces.
Related
I'm trying to get values from a XML document using the iXF format, but I'm having some issues with the XPath syntax.
I have the following XML document
<SOAP_ENV:Envelope xmlns:NS2="http://www.ixfstd.org/std/ns/core/classBehaviors/links/1.0" xmlns:NS1="CATIA/V5/Electrical/1.0" xmlns:tns="IXF_Schema.xsd" xmlns:ixf="http://www.ixfstd.org/std/ns/core/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP_ENV="http://schemas.xmlsoap.org/soap/envelope/" xsi:schemaLocation="IXF_Schema.xsd ElectricalSchema.xsd">
<SOAP_ENV:Body>
<ixf:object id="Electrical Physical System00000089.1" xsi:type="tns:Harness">
<tns:Name>Electrical Physical System00000089.1</tns:Name>
</ixf:object>
<ixf:object id="X10(1)//X11(1)" xsi:type="tns:Wire">
<tns:Name>X10(1)//X11(1)</tns:Name>
<NS1:Wire>
<NS1:Length>763,752mm</NS1:Length>
<NS1:Color>RD</NS1:Color>
<NS1:OuterDiameter>1,32mm</NS1:OuterDiameter>
</NS1:Wire>
</ixf:object>
</SOAP_ENV:Body>
</SOAP_ENV:Envelope>
And i'm trying to find all the Wire objects and get the Name and Length values with the following code.
XmlDocument xlDocument = new XmlDocument();
xlDocument.Load(importFile);
XmlNamespaceManager nsManager = new XmlNamespaceManager(xlDocument.NameTable);
nsManager.AddNamespace("tns", "IXF_Schema.xsd");
nsManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
nsManager.AddNamespace("ixf", "http://www.ixfstd.org/std/ns/core/1.0");
nsManager.AddNamespace("NS1", "CATIA/V6/Electrical/1.0");
nsManager.AddNamespace("NS2", "http://www.ixfstd.org/std/ns/core/classBehaviors/links/1.0");
//Get all wire objects
XmlNodeList wires = xlDocument.SelectNodes("descendant::ixf:object[#xsi:type = \"tns:Wire\"]", nsManager);
foreach (XmlNode wire in wires)
{
string wireName;
string wireLength;
XmlNode node = wire.SelectSingleNode("./tns:Name", nsManager);
wireName = node.InnerText;
XmlNode node1 = wire.SelectSingleNode("./NS1:Wire/NS1:Length", nsManager);
wireLength = node1.InnerText;
}
I can get the wireName value without any problems but the Length element selection always returns 0 matches and I can not figure out why. I also tried to only select the Wire element using the same syntax as the Name element ./NS1:Wire but that also returns 0 matches.
Your XML declares
xmlns:NS1="CATIA/V5/Electrical/1.0"
^^
Your C# declares a different namespacem
nsManager.AddNamespace("NS1", "CATIA/V6/Electrical/1.0")
^^
Make sure both namespaces match exactly.
Regarding your comment asking about the use of version numbers in namespaces...
It is an unfortunately common but certainly not widely accepted practice to include a version number in an XML namespace. Realize that by doing so, you're effectively saying that every namespaced XML component (element or attribute) should now be considered to differ from its counterpart in the old namespace. This is rarely what you want.
See also
Should I use a Namespace of an XML file to identify its version
What are the best practices for versioning XML schemas?
I'm attempting to replicate a dialogue system from a game that has control codes, which are HTML/XML-like tags that dictate behavior of a text bubble. For example, changing the color of a piece of text would be like <co FF0000FF>Hello World!</co>. These control codes are not required in the text, so Hello <co FF0000FF>World!</co> or simply Hello World should parse as well.
I've attempted to make it similar to XML to ease parsing, but XML requires a root-level tag to parse successfully, and the text may or may not have any control codes. For example, I'm able to parse the following fine with XElement.
string Text = "<co value=\"FF0000FF\">Hello World!</co>"
XElement.Parse(Text);
However, the following fails with an XMLException ("Data at the root level is invalid. Line 1, position 1."):
string Text = "Hello <co value=\"FF0000FF\">World!</co>"
XElement.Parse(Text);
What would be a good approach to handling this? Is there a way to handle parsing XML elements in a string without requiring a strict XML syntax, or is there another type of parser I can use to achieve what I want?
If the only difference between your XML-like fragments and real XML is the absence of a root element, then simply wrap the fragment in a dummy root element before parsing:
parse("<dummy>" + fragment + "</dummy>")
If there are other differences, for example attributes not being in quotes, or attribute names starting with a digit, then an XML parser isn't going to be much use to you, you will need to write your own. Or an HTML parser such as validator.nu might handle it, if you're lucky.
You can try with HtmlAgilityPack
Install Nuget packge by firing this command Install-Package HtmlAgilityPack
The following sample will return all the child nodes. I did not pass any level to Descendants but you can further put more code as per need.
It will parse your custom format.
string Text = "Hello <co value=\"FF0000FF\">World!</co>";
Text = System.Net.WebUtility.HtmlDecode(Text);
HtmlDocument result = new HtmlDocument();
result.LoadHtml(Text);
List<HtmlNode> nodes = result.DocumentNode.Descendants().ToList();
If the XML elements within your text will always be well-formed, then you can use the XML libraries to do this.
You can either wrap your text inside a root element and use XElement.Parse and read the child nodes, or you can use some lower level bits to allow you to parse the nodes in an XML fragment:
public static IEnumerable<XNode> Parse(string text)
{
var settings = new XmlReaderSettings
{
ConformanceLevel = ConformanceLevel.Fragment
};
using (var sr = new StringReader(text))
using (var xr = XmlReader.Create(sr, settings))
{
xr.MoveToContent();
while (xr.EOF == false)
{
yield return XNode.ReadFrom(xr);
}
}
}
Using it like this:
foreach (var node in Parse("Hello <co value=\"FF0000FF\">World!</co>"))
{
Console.WriteLine($"{node.GetType().Name}: {node}");
}
Would output this:
XText: Hello
XElement: <co value="FF0000FF">World!</co>
See this fiddle for a working demo.
i am trying to create an application which would output the data present at some xpath(which will be specified by user)
XPathDocument xmldoc = new XPathDocument(file);
XPathNavigator nav = xmldoc.CreateNavigator();
XPathNavigator result = nav.SelectSingleNode("//p");
MessageBox.Show(result.Value);
Here variable file is location of xml file.
Now when I am running this code on a xml file that has lots of namespaces defined on it , the above code returns nullreference exception, because variable result is null and i am trying to access
result.Value .
But when i created my own xml file
<a>
<b>
<p>abc</p>
</b>
</a>
the codes runs fine .
So, what i am inferring is that the problem is because i am not including the namespaces in the code.
I searched and found out the suggestion that the way to trick namespaces is to use relative xpaths such as //p .Here
What is a good way to find a specific value in an XML document using C#?
But the code still does not works on the original file (one containing the namespaces)
How about:
XPathNavigator result = nav.SelectSingleNode("//*[local-name()='p']");
I have an incoming file with data as
<root><![CDATA[<defs><elements>
<element><item>aa</item><int>1</int></element>
<element><item>bb</item><int>2</int></element>
<element><item>cc</item><int>3</int></element>
</elements></defs>]]></root>
writing multiple foreach( xElement x in root.Elements ) seems superfluous !
looking for a less verbose method preferably using C#
UPDATE - yes - the input is in a CDATA, rest assured it's not my design and i have ZERO control over it !
Assuming that nasty CDATA section is intentional, and you're only interested in the text content of your leaf elements, you can do something like:
XElement root = XElement.Load(yourFile);
var data = from element in XElement.Parse(root.Value).Descendants("element")
select new {
Item = element.Elements("item").First().Value,
Value = element.Elements("int").First().Value
};
That said, if the code that generates your input file is under your control, consider getting rid of the CDATA section. Storing XML within XML that way is not the way to go most of the time, as it defeats the purpose of the markup language (and requires multiple parser passes, as shown above).
I am using Linq To XML to create XML that is sent to a third party. I am having difficulty understanding how to create the XML using Linq when part of information I want to send in the XML will be dynamic.
The dynamic part of the XML is held as a string[,] array. This multi dimensional array holds 2 values.
I can 'build' the dynamic XML up using a stringbuilder and store the values that were in the array into a string variable but when I try to include this variable into Linq the variable is HTMLEncoded rather than included as proper XML.
How would I go about adding in my dynamically built string to the XML being built up by Linq?
For Example:
//string below contains values passed into my class
string[,] AccessoriesSelected;
//I loop through the above array and build up my 'Tag' and store in string called AccessoriesXML
//simple linq to xml example with my AccessoriesXML value passed into it
XDocument RequestDoc = new XDocument(
new XElement("MainTag",
new XAttribute("Innervalue", "2")
),
AccessoriesXML);
'Tag' is an optional extra, it might appear in my XML multiple times or it might not - it's dependant on a user checking some checkboxes.
Right now when I run my code I see this:
<MainTag> blah blah </MainTag>
< ;Tag> ;< ;InnerTag> ; option1="valuefromarray0" option2="valuefromarray1" /> ;< ;Tag/> ;
I want to return something this:
<MainTag> blah blah </MainTag>
<Tag><InnerTag option1="valuefromarray0" option2="valuefromarray1" /></Tag>
<Tag><InnerTag option1="valuefromarray0" option2="valuefromarray1" /></Tag>
Any thoughts or suggestions? I can get this working using XmlDocument but I would like to get this working with Linq if it is possible.
Thanks for your help,
Rich
Building XElements with the ("name", "value") constructor will use the value text as literal text - and escape it if necessary to achieve that.
If you want to create the XElement programatically from a snippet of XML text that you want to actually be interpreted as XML, you should use XElement.Load(). This will parse the string as actual XML, instead of trying to assign the text of the string as an escaped literal value.
Try this:
XDocument RequestDoc = new XDocument(
new XElement("MainTag",
new XAttribute("Innervalue", "2")
),
XElement.Load(new StringReader(AccessoriesXML)));