What is happening \ what is the difference ?
I'm trying to return a specific node from an XML File.
XML File:
<?xml version="1.0" encoding="utf-8"?>
<JMF SenderID="InkZone-Controller" Version="1.2">
<Command ID="cmd.00695" Type="Resource">
<ResourceCMDParams ResourceName="InkZoneProfile" JobID="K_41">
<InkZoneProfile ID="r0013" Class="Parameter" Locked="false" Status="Available" PartIDKeys="SignatureName SheetName Side Separation" DescriptiveName="Schieberwerte von DI" ZoneWidth="32">
<InkZoneProfile SignatureName="SIG1">
<InkZoneProfile Locked="False" SheetName="S1">
<InkZoneProfile Side="Front" />
</InkZoneProfile>
</InkZoneProfile>
</InkZoneProfile>
</ResourceCMDParams>
</Command>
<InkZoneProfile Separation="Cyan" ZoneSettingsX="0 0,005 " />
</JMF>
Code:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("C:\\test\\test.xml");
XmlNode root = xmlDoc.DocumentElement;
var parent = root.SelectSingleNode("/JMF/Command/ResourceCmdParams/InkZoneProfile/InkZoneProfile/InkZoneProfile/InkZoneProfile");
XmlElement IZP = xmlDoc.CreateElement("InkZoneProfile");
IZP.SetAttribute("Separation", x.colorname);
IZP.SetAttribute("ZoneSettingsX", x.colorvalues);
xmlDoc.DocumentElement.AppendChild(IZP);
xmlDoc.Save("C:\\test\\test.xml");
The var parent returns me null. I've debugged , and root and xmlDoc have on their inner text the XML Content.
But, a test made here(made by user #har07 , on the previous question:
SelectSingleNode returns null even with namespace managing
Worked without problems.
https://dotnetfiddle.net/vJ8h9S
What is the difference between those two ? They follow the same code basically, but one works and other doesn't.
When debugging i've found that root.InnerXml has the contents loaded on itself (same as XmlDoc.InnerXml ). But InnerXml doesn't implement a method to SelectSingleNode. I believe that if i save it to a string i'll probably lose indentation and etc.
Can someone tell me what is the difference or what is wrong ? Thanks !
XML Sample: https://drive.google.com/file/d/0BwU9_GrFRYrTUFhMYWk5blhhZWM/view?usp=sharing
SetAttribute don't auto escape string for you. Therefore it make your XML file invalid.
From MSDN about XmlElement.SetAttribute
Any markup, such as syntax to be recognized as an entity reference, is treated as literal text and needs to be properly escaped by the implementation when it is written out
Find in your code all line contain SetAttribute and use SecurityElement.Escape to escape the value.
For example: Change these lines:
IZP.SetAttribute("Separation", x.colorname);
IZP.SetAttribute("ZoneSettingsX", x.colorvalues);
To:
using System.Security;
IZP.SetAttribute("Separation", SecurityElement.Escape(x.colorname));
IZP.SetAttribute("ZoneSettingsX", SecurityElement.Escape(x.colorvalues));
If an attribute have name contains any of <>"'& you also have to escape it like the value.
Note:
You have to delete current xmls you create used the old code, because it is invalid, when you load it will cause exception.
Related
I have following XML:
<?xml version="1.0" encoding="utf-16"?>
<cincinnati xmlns="http://www.sesame-street.com/abc/def/1">
<cincinnatiChild xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ElementValue xmlns:a="http://schemas.data.org/2004/07/sesame-street.abc.def.ghi">
<a:someField>false</a:someField>
<a:data xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<b:KeyValueThing>
<b:Key>key1</b:Key>
<b:Value i:type="a:ArrayOfPeople">
<a:Person>
<a:firstField>
</a:firstField>
<a:dictionary>
<b:KeyValueThing>
<b:Key>ID</b:Key>
<b:Value i:type="c:long" xmlns:c="http://www.w3.org/2001/XMLSchema">000101</b:Value>
</b:KeyValueThing>
<b:KeyValueThing>
<b:Key>Name</b:Key>
<b:Value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">John</b:Value>
</b:KeyValueThing>
</a:dictionary>
</a:Person>
<a:Person>
...
<b:Value i:type="c:long" xmlns:c="http://www.w3.org/2001/XMLSchema">000102</b:Value>
...
</a:Person>
</b:Value>
</b:KeyValueThing>
</a:data>
</ElementValue>
</cincinnatiChild>
</cincinnati>
I need to get a list if ID values, e.g. 000101, 000102....
I think using XPath makes sense here but the multitude of namespaces makes it confusing (so a simple XmlNamespaceManager won't do).
Basically I need something like this (this syntax is of course not correct):
XmlDocument doc = // Load the xml
doc.DocumentElement.SelectSingleNode("/cincinati/cincinnatiChild/ElementValue/a:data/b:KeyValueThing/b:Value/a:Person/a:dictionary[b:KeyValueThing/b:Key='ID']");
also when I do doc.DocumentElement.SelectSingleNode("/cincinnati") or doc.DocumentElement.SelectSingleNode("/cincinnatiChild") I get null.
Since I'm unsure how to piece together al the helpfull advice from the comments I would like to see a working c# code line, either XmlDocument or XDocument are OK.
I also tries this before the SelectSingleNode:
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("a", "http://schemas.data.org/2004/07/sesame-street.abc.def.ghi");
nsmgr.AddNamespace("b", "http://schemas.microsoft.com/2003/10/Serialization/Arrays");
nsmgr.AddNamespace("c", "http://www.w3.org/2001/XMLSchema");
nsmgr.AddNamespace("i", "http://www.w3.org/2001/XMLSchema-instance");
nsmgr.AddNamespace("d", "http://www.sesame-street.com/abc/def/1");
You could bypass the namespace issues using the local-name()-function.
i.e.: in stead of b:KeyValueThing use *[local-name()='KeyValueThing']
a simple global XPath could look like this:
//*[local-name()='KeyValueThing'][*[local-name()='Key' and text()='ID' ]]/*[local-name()='Value']/text()
If you want to be more precise and speed up the XPath it would look like this:
/*[local-name()='cincinnati']/*[local-name()='cincinnatiChild']/*[local-name()='ElementValue']/*[local-name()='data']/*[local-name()='KeyValueThing']/*[local-name()='Value']/*[local-name()='Person']/*[local-name()='dictionary']/*[local-name()='KeyValueThing'][*[local-name()='Key' and text()='ID' ]]/*[local-name()='Value']/text()
I am tasked with ripping and stripping pertinent data from XFDL files. I am attempting to use XmlDocument's SelectSignleNode method to do so. However, it has proven unsuccessful.
Represntative XML:
<XFDL>
...
<page1>
<check3>true</check3>
</page1>
...
<page sid="PAGE1">
<check sid="CHECK9">
<value>true</value>
</check>
</page>
...
Code:
XmlDocument document = new XmlDocument();
document.Load(memoryStream);//decoded and unzipped xfdl file
//Doesn't work
XmlNode checkBox = document.SelectSingleNode("//check[#sid='CHECK9']/value");
//Doesn't work
XmlNode checkBox = document.SelectSingleNode("//page[#sid='PAGE1']/check[#sid='CHECK9']");
MsgBox(checkBox.InnerXml);
Yields me System.NullReferenceException as an XmlNode isn't selected.
I think I'm having an xpath issue but I can't seem to understand where. The earlier xml node is easily selected using:
XmlNode checkBox = document.SelectSingleNode("//page1/check3");
MsgBox(checkBox.InnerText);
Displays just fine. And just to head it off at the pass, there isn't a definition of <check9></check9> in the <page1> tag.
Anyone have some insight?
Thanks in advance.
Okay, so here's the deal. XFDL defines a default namespace that requires an arbitrary mapping for xpath querying. In my case:
XML:
<XFDL xmlns="http://www.ibm.com/xmlns/prod/xfdl/8.0" ... >
Code:
manager.AddNamespace("a", "http://www.ibm.com/xmlns/prod/xfdl/8.0");
//Append 'a:' to query elements
document.SelectSingleNode("//a:check[#sid='CHECK9']/a:value", manager);
The problem is compounded because <check> is buried in <page> which is defined in another namespace: xfdl. My xpath query becomes:
document.SelectSingleNode("//xfdl:page[#sid='PAGE1']/a:check[#sid='CHECK9']/a:value", manager);
Now this is rather XFDL specific but can be applied to other issues where there are multiple namespaces defined within an XML document.
EDIT 1
Source: http://codeclimber.net.nz/archive/2008/01/09/How-to-query-a-XPath-doc-that-has-a-default.aspx
Thank you all for suggesting things and helping whenever in need.
Yesterday I was trying to develop and web app in asp.net 4.0 where I needed to parse the data from xml and save it in database. But before that I will also have to validate it.
I tried using .net provided tool xsd.exe to generate the schema file, but I dont know how will it know to mark which nodes or attributes are compulsory?
Like in my xml below items are mandatory
Root node <Market>
<Login> and its sub element
<ProductType> and its <ProductTypeID/>
The attribute DML is mandatory but should have only 3 values NONE, PUT or MODIFY
<ProductType> may or may not have <ProductItem>
If <ProductItem> is present then it should have <ProductItemID>
<ProductItem> may or may not have <Brand>
If <Brand> is present then it should have <BrandID>
Below is my xml
<?xml version="1.0" encoding="utf-8" ?>
<Market>
<Login>
<LoginId />
<Password />
</Login>
<ProductType DML="NONE">
<ProductTypeID/>
<Name/>
<Detail/>
<ProductItem DML="PUT">
<ProductItemID/>
<Name/>
<Detail/>
<Brand DML="PUT">
<BrandID/>
<Name/>
<Detail/>
</Brand>
<Brand DML="MODIFY">
<BrandID/>
<Name/>
<Detail/>
</Brand>
</ProductItem>
<ProductItem DML="MODIFY">
<ProductItemID/>
<Name/>
<Detail/>
</ProductItem>
</ProductType>
</Market>
How and where should I specify all the mandatory and optional parameters, so that my xsd is generated as per the requirement.
Thanks,
M.
xsd.exe can only try to infer, which elements/attributes are in you xml, but it cannot find out which information is mandatory. But this is a good startingpoint.
use a graphical XML_Schema_Editor to edit the genrated xsd to mark your mandatory fields. That is much easier than learning the xsd-language
I don't think XSD support nested XML. I would try to load the XML into an XmlDocument and check mandatory fields manually.
I'm trying to do very simple operations on a .cxml file. As you know it's basically an .xml file. This is a sample file I created to test the application:
<?xml version="1.0" encoding="utf-8"?>
<Collection xmlns:p="http://schemas.microsoft.com/livelabs/pivot/collection/2009" SchemaVersion="1.0" Name="Actresses" xmlns="http://schemas.microsoft.com/collection/metadata/2009">
<FacetCategories>
<FacetCategory Name="Nationality" Type="LongString" p:IsFilterVisible="true" p:IsWordWheelVisible="true" p:IsMetaDataVisible="true" />
</FacetCategories>
<!-- Other entries-->
<Items ImgBase="Actresses_files\go144bwo.0ao.xml" HrefBase="http://www.imdb.com/name/">
<Item Id="2" Img="#2" Name="Anna Karina" Href="nm0439344/">
<Description> She is a nice girl</Description>
<Facets>
<Facet Name="Nationality">
<LongString Value="Danish" />
</Facet>
</Facets>
</Item>
</Items>
<!-- Other entries-->
</Collection>
I can't get any functioning simple code like:
XDocument document = XDocument.Parse(e.Result);
foreach (XElement x in document.Descendants("Item"))
{
...
}
The test on a generic xml is working. The cxml file is correctly loaded in document.
While watching the expression:
document.Descendants("Item"), results
the answer is:
Empty "Enumeration yielded no results" string
Any hint on what can be the error? I've also add a quick look to get Descendants of Facet, Facets, etc., but there are no results in the enumeration. This obviously doesn't happen with a generic xml file I used for testing. It's a problem I have with .cxml.
Basically your XML defines a default namespace with the xmlns="http://schemas.microsoft.com/collection/metadata/2009" attribute:
That means you need to fully qualify your Descendants query e.g.:
XDocument document = XDocument.Parse(e.Result);
foreach (XElement x in document.Descendants("{http://schemas.microsoft.com/collection/metadata/2009}Item"))
{
...
}
If you remove the default namespace from the XML your code actually works as-is, but that is not the aim of the exercise.
See Metadata.CXML project under http://github.com/Zoomicon/Metadata.CXML sourcecode for LINQ-based parsing of CXML files.
Also see ClipFlair.Metadata project at http://github.com/Zoomicon/ClipFlair.Metadata for parsing one's CXML custom facets too
BTW, at http://ClipFlair.codeplex.com can checkout the ClipFlair.Gallery project for how to author ASP.net web-based forms to edit metadata fragments (parts of CXML files) and merge them together in a single one (that you then convert periodically to DeepZoom CXML with PAuthor tool from http://pauthor.codeplex.com).
If anyone is interested in doing nesting (hierarchy) of CXML collections see
http://github.com/Zoomicon/Trafilm.Metadata
and
http://github.com/Zoomicon/Trafilm.Gallery
I just ran into an issue where my code was parsing xml fine but once I added in a second node it started to load incorrect data. The real code spans a number of classes and projects but for the sample I've put together the basics of what's causing the issue
When the code runs I'd expect the output to be the contents of the second Task node, but instead the contents of the first node is output. It keeps pulling from the first occurrence of the EmailAddresses node despite how when you check the settings object its inner xml is that of the second Task node. The call to SelectSingleNode("//EmailAddresses") is where the issue happens.
I have two ways around this issue
Remove the leading slashes from the EmailAddresses XPath expression
Call Clone() after getting the Task or Settings node
Solution 1 works in this case but I believe this will cause other code in my project to stop working.
Solution 2 looks more like a hack to me than a real solution.
MY question is am I in fact doing this correctly and there's a bug in .NET (all versions) or am I just pulling the XML wrong?
The c# code
var doc = new XmlDocument();
doc.Load(#"D:\temp\Sample.xml");
var tasks = doc.SelectSingleNode("Server/Tasks");
foreach (XmlNode threadNode in tasks.ChildNodes)
{
if (threadNode.Name.ToLower() != "thread")
{
continue;
}
foreach (XmlNode taskNode in threadNode.ChildNodes)
{
if (taskNode.Name.ToLower() != "task" || taskNode.Attributes["name"].Value != "task 1")
{
continue;
}
var settings = taskNode.SelectSingleNode("Settings");
var emails = settings.SelectSingleNode("//EmailAddresses");
Console.WriteLine(emails.InnerText);
}
}
The XML
<?xml version="1.0"?>
<Server>
<Tasks>
<Thread>
<Task name="task 2">
<Settings>
<EmailAddresses>task 2 data</EmailAddresses>
</Settings>
</Task>
</Thread>
<Thread>
<Task name="task 1">
<Settings>
<EmailAddresses>task 1 data</EmailAddresses>
</Settings>
</Task>
</Thread>
</Tasks>
</Server>
From http://www.w3.org/TR/xpath/#path-abbrev
// is short for
/descendant-or-self::node()/. For
example, //para is short for
/descendant-or-self::node()/child::para
and so will select any para element in
the document (even a para element that
is a document element will be selected
by //para since the document element
node is a child of the root node);
And also:
A location step of . is short for
self::node(). This is particularly
useful in conjunction with //. For
example, the location path .//para
is short for
self::node()/descendant-or-self::node()/child::para
and so will select all para descendant
elements of the context node.
Instead of:
var settings = taskNode.SelectSingleNode("Settings");
var emails = settings.SelectSingleNode("//EmailAddresses");
Use:
var emails = taskNode.SelectSingleNode("Settings/EmailAddresses");
The // XPath expression does not do what you think it does. It selects nodes in the document from the current node that match the selection no matter where they are.
In other words, it's not limited by the current scope, it actually crawls back up the document tree and starts matching from the root element.
To select the first <EmailAddresses> element in your current scope, you only need:
var emails = settings.SelectSingleNode("EmailAddresses");