Very confused about how to parse xml with namespace prefixes - asp.net

So, I need to be able to parse xml files that could include namespace prefixes. I've tried doing this with a sample file and it gives me back null when trying to get a nodelist, even when I specify a node that has no attribute prefixes.
I've been trying to research this and it keeps coming back to the fact that without the namespace prefix defined, it won't work, so I've added code that I thought would do this, but it's still giving the same results. Here's some code I've added:
protected void Page_Load(object sender, EventArgs e)
{
xml.Load(Server.MapPath("~/SomeLesson/imsmanifest.xml"));
populateBaseNodes();
}
private void populateBaseNodes()
{
treeViewMenu.Nodes.Clear(); // Clear any existing items
TreeNode treenode = new TreeNode();
treenode.Text = "organizations";
XmlNodeList baseNodeList;
string xmlns = xml.DocumentElement.Attributes["xmlns"].Value;
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xml.NameTable);
nsmgr.AddNamespace("adlcp", "http://www.adlnet.org/xsd/adlcp_v1p3");
nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
nsmgr.AddNamespace("imscp", "http://www.w3.org/2001/XMLSchema-instance");
nsmgr.AddNamespace("imsss", "http://www.w3.org/2001/XMLSchema-instance");
nsmgr.AddNamespace("schemaLocation", "http://www.w3.org/2001/XMLSchema-instance");
baseNodeList = xml.SelectNodes("/manifest/organizations/organization/item", nsmgr);
TextBox1.Text = baseNodeList.Count.ToString();
foreach (XmlNode xmlnode in baseNodeList)
{
TreeNode treeNodeCatalog = new TreeNode();
treeNodeCatalog.Text = xmlnode.Attributes["identifier"].Value;
treeNodeCatalog.SelectAction = TreeNodeSelectAction.Expand;
treeViewMenu.Nodes.Add(treeNodeCatalog);
}
treeViewMenu.CollapseAll();
}
(marc_s) Here's the XML in question that needs to be parsed:
<manifest identifier="Navigating_in_QuickBooks_-_Introduction_MANIFEST" version="1.3"
xmlns="http://www.imsglobal.org/xsd/imscp_v1p1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:imscp="http://www.imsglobal.org/xsd/imscp_v1p1"
xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_v1p3"
xmlns:imsss="http://www.imsglobal.org/xsd/imsss"
xsi:schemaLocation=" http://www.imsglobal.org/xsd/imscp_v1p1 imscp_v1p1.xsd
http://www.imsglobal.org/xsd/imsss imsss_v1p0.xsd
http://www.adlnet.org/xsd/adlcp_v1p3 adlcp_v1p3.xsd
http://www.adlnet.org/xsd/adlseq_v1p3 adlseq_v1p3.xsd
http://www.adlnet.org/xsd/adlnav_v1p3 adlnav_v1p3.xsd">
<metadata>
<!-- not relevant here ... -->
</metadata>
<organizations default="TOC1">
<organization identifier="TOC1">
<title>Navigating in QuickBooks - Introductory Lesson</title>
<item identifier="I_SCO0" identifierref="SCO0">
<title>Navigating in QuickBooks - Introductory Lesson</title>
</item>
</organization>
</organizations>
<resources>
<!-- not relevant here ... -->
</resources>
</manifest>

You're not showing us what your XML looks like - but two comments:
you don't need to add the xsi prefix, and I'm not sure what the schemaLocation prefix is supposed to do ....
when you've defined the schema prefixes, you also need to use those prefixes in your XPath, of course!
Again, not knowing what your XML structure looks like, I cannot really tell what you need - but something along the lines of:
baseNodeList = xml.SelectNodes("/adlcp:manifest/adlcp:organizations/adlcp:organization/imscp:item", nsmgr);
or whatever other XML namespace prefixes your source XML requires.
Update: seeing your XML makes it clearer: see the root node - it has a default XML namespace (the one with xmlns="...." and no explicit prefix):
<manifest identifier="Navigating_in_QuickBooks_-_Introduction_MANIFEST" version="1.3"
xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" <=== DEFAULT namespace!!!
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:imscp="http://www.imsglobal.org/xsd/imscp_v1p1"
xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_v1p3"
xmlns:imsss="http://www.imsglobal.org/xsd/imsss"
................>
That means: ALL your subsequent nodes that don't have a specific XML prefix will be in that default namespace.
Unfortunately, .NET XML parsing has problem with defining a default namespace without prefix - so my best solution is to create a namespace with a prefix for the default namespace, and then use it:
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xml.NameTable);
// add default namespace, with a prefix for .NET
nsmgr.AddNamespace("ns", "http://www.imsglobal.org/xsd/imscp_v1p1");
baseNodeList =
xml.SelectNodes("/ns:manifest/ns:organizations/ns:organization/ns:item", nsmgr);
Do you get any results now??

In the XML you posted the default namespace controlling all of the elements in your sample file is:
xmlns="http://www.imsglobal.org/xsd/imscp_v1p1"
This namespace does not define a prefix so you must add this namespace to your namespace manager using a blank prefix. I think you should be able to use this code to define the default namespace (using String.Empty to specify a blank prefix):
nsmgr.AddNamespace(String.Empty, "http://www.imsglobal.org/xsd/imscp_v1p1");

Related

SelectSingleNode function returns nothing from XML document [duplicate]

How to parse the xml file?
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>link</loc>
<lastmod>2011-08-17T08:23:17+00:00</lastmod>
</sitemap>
<sitemap>
<loc>link</loc>
<lastmod>2011-08-18T08:23:17+00:00</lastmod>
</sitemap>
</sitemapindex>
I am new to XML, I tried this, but it seems to be not working :
XmlDocument xml = new XmlDocument(); //* create an xml document object.
xml.Load("sitemap.xml");
XmlNodeList xnList = xml.SelectNodes("/sitemapindex/sitemap");
foreach (XmlNode xn in xnList)
{
String loc= xn["loc"].InnerText;
String lastmod= xn["lastmod"].InnerText;
}
The problem is that the sitemapindex element defines a default namespace. You need to specify the namespace when you select the nodes, otherwise it will not find them. For instance:
XmlDocument xml = new XmlDocument();
xml.Load("sitemap.xml");
XmlNamespaceManager manager = new XmlNamespaceManager(xml.NameTable);
manager.AddNamespace("s", "http://www.sitemaps.org/schemas/sitemap/0.9");
XmlNodeList xnList = xml.SelectNodes("/s:sitemapindex/s:sitemap", manager);
Normally speaking, when using the XmlNameSpaceManager, you could leave the prefix as an empty string to specify that you want that namespace to be the default namespace. So you would think you'd be able to do something like this:
// WON'T WORK
XmlDocument xml = new XmlDocument();
xml.Load("sitemap.xml");
XmlNamespaceManager manager = new XmlNamespaceManager(xml.NameTable);
manager.AddNamespace("", "http://www.sitemaps.org/schemas/sitemap/0.9"); //Empty prefix
XmlNodeList xnList = xml.SelectNodes("/sitemapindex/sitemap", manager); //No prefixes in XPath
However, if you try that code, you'll find that it won't find any matching nodes. The reason for this is that in XPath 1.0 (which is what XmlDocument implements), when no namespace is provided, it always uses the null namespace, not the default namespace. So, it doesn't matter if you specify a default namespace in the XmlNamespaceManager, it's not going to be used by XPath, anyway. To quote the relevant paragraph from the Official XPath Specification:
A QName in the node test is expanded into an expanded-name using the
namespace declarations from the expression context. This is the same
way expansion is done for element type names in start and end-tags
except that the default namespace declared with xmlns is not used: if
the QName does not have a prefix, then the namespace URI is null (this
is the same way attribute names are expanded). It is an error if the
QName has a prefix for which there is no namespace declaration in the
expression context.
Therefore, when the elements you are reading belong to a namespace, you can't avoid putting the namespace prefix in your XPath statements. However, if you don't want to bother putting the namespace URI in your code, you can just use the XmlDocument object to return the URI of the root element, which in this case, is what you want. For instance:
XmlDocument xml = new XmlDocument();
xml.Load("sitemap.xml");
XmlNamespaceManager manager = new XmlNamespaceManager(xml.NameTable);
manager.AddNamespace("s", xml.DocumentElement.NamespaceURI); //Using xml's properties instead of hard-coded URI
XmlNodeList xnList = xml.SelectNodes("/s:sitemapindex/s:sitemap", manager);
Sitemap has 2 sub nodes "loc" and "lastmod". The nodes that you are accessing are "name" and "url". that is why you are not getting any result. Also in your XML file the last sitemap tag is not closed properly with a corresponding Kindly try xn["loc"].InnerText and see if you get the desired result.
I would definitely use LINQ to XML instead of the older XmlDocument based XML API. You can accomplish what you are looking to do using the following code. Notice, I changed the name of the element that I am trying to get the value of to 'loc' and 'lastmod', because this is what is in your sample XML ('name' and 'url' did not exist):
XElement element = XElement.Parse(XMLFILE);
IEnumerable<XElement> list = element.Elements("sitemap");
foreach (XElement e in list)
{
String LOC= e.Element("loc").Value;
String LASTMOD = e.Element("lastmod").Value;
}

How to add Xml File Into Project(after deployment) Using User Interface?

I have one DemoQuestion.Xml,after deploying project i want to upload DemoQuestion.xml file with different Question Using USer Interface(such as Admin add New Question To test)
DemoQuestion.xml
<?xml version="1.0" encoding="UTF-8"?>
<quiz xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="quiz.xsd">
<mchoice>
<question>Sum of 20 and 30?</question>
<answer>20</answer>
<answer correct="yes">50</answer>
<answer>10</answer>
<answer>11</answer>
</mchoice>
<mchoice>
</quiz>
i want to add <answer Correct="yes">This attribute in xml how to do it?
Here's a minimalistic code snippet...
string file = Server.MapPath("~/file.xml");
XmlDocument doc = new XmlDocument();
doc.Load(file);
var answers = doc.SelectNodes("//answer");
if (answers != null && answers.Count > 0)
{
XmlAttribute attr = doc.CreateAttribute("correct");
attr.Value = "yes";
answers[0].Attributes.Append(attr);
}
doc.Save(file);
All it does is load the document from a file, retrieves all answer elements and adds the "correct" attribute with the value of "yes" to the first answer found.
Hope it helps illustrate the solution
Leo

How to ignore comments when parsing xml in asp.net

Seems like this should be easy, but I'm not finding a simple configuration setting. Basically I have a page that will be parsing xml files that may have some comment tags in them. I'm loading it as an xml doc and looping through a particular node of the document and I'm running into problems because it's counting the comment as a child node. Any way to tell asp.net not to look at comments other than writing my own check for <!-- ?
If you use XmlNode, then that has a NodeType property. Ignore the nodes where that has a value of "Comment".
An XNode has the same property.
Use XmlReaderSettings.IgnoreComments:
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.IgnoreComments = true;
using (XmlReader reader = XmlReader.Create("input.xml", readerSettings))
{
XmlDocument myData = new XmlDocument();
myData.Load(reader);
// etc...
}

How do you properly use xmlns namespaces with custom extensions to .NET SyndicationFeeds?

I am using the .NET SyndicationFeed class and have added some of my own extensions using SyndicationItem.ElementExtensions.Add() as well as setting SyndicationItem.Content to some Xml content.
My problem is that my namespace shows up multiple times in the XML output. Ideally I would apply a xmlns attribute to the root node and use its alias throughout the document.
I have seen examples that discuss using SyndicationFeed.AttributeExtensions as seen here. For example:
feed.AttributeExtensions.Add(
new System.Xml.XmlQualifiedName("myns", "http://www.w3.org/2000/xmlns"),
"http://myNamespace.com");
But, none of these examples show how to utilize the namespace later. For example, here are two ways I extend the feed:
XNamespace myNs = "http://myNamespace.com";
SyndicationItem item = new SyndicationItem();
XElement myMetadata = new XElement(myNs + "metadata");
myMetadata.Add(new XElement(myNs + "meta1", "value1"));
myMetadata.Add(new XElement(myNs + "meta2", "value2"));
item.Content = SyndicationContent.CreateXmlContent(myMetadata);
XElement myExtensions = new XElement(myNs + "myExtensions");
myExtensions.Add(new XElement(myNs + "ext1", "value1"));
myExtensions.Add(new XElement(myNs + "ext2", "value2"));
item.ElementExtensions.Add(myExtensions);
Hopefully I'm missing something simple. With the AttribuetExtensions.Add() method further above, my feed has the following for the initial XML:
<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
<channel p3:myns="http://myNamespace.com" xmlns:p3="http://www.w3.org/2000/xmlns">
Granted, I'd prefer that the xmlns for myns be on the root rss node and not the channel, but I can live with it being on the channel. Unfortunately, the syndication item xml looks like:
<item>
...
<a10:content type="text/xml">
<metadata xmlns="http://myNamespace.com">
<meta1>value1</meta2>
<meta2>value2</meta2>
</metadata>
</a10:content>
<myExtensions xmlns="http://myNamespace.com">
<ext1>value1</ext1>
<ext2>value2</ext2>
</myExtensions>
</item>
Of course, what I'd prefer to see is:
<item>
...
<a10:content type="text/xml">
<myns:metadata>
<meta1>value1</meta2>
<meta2>value2</meta2>
</myns:metadata>
</a10:content>
<myns:myExtensions>
<ext1>value1</ext1>
<ext2>value2</ext2>
</myns:myExtensions>
</item>
Is there some special way of linking the namespace defined by SyndicationFeed.AttributeExtensions with that used when extending a SyndicationItem?
You may want to decalre the namespace for the feed like so:
new XmlQualifiedName("rdf", "http://www.w3.org/2000/xmlns/"), "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
Then you can declare your XNamespace and use it in your element creation.
XNamespace Rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
item.ElementExtensions.Add(
new XElement(Rdf + "type",
new XAttribute(Rdf + "resource", "attribute value")));
I have created one here for an rdf element but you can do it for any of your custom types. You could even add the creation of your namespaces into an extension method of the SyndicationFeed and SyndicationItem:
public static void SetNamespace(this SyndicationFeed feed, string prefix, string nsUri)
{
feed.AttributeExtensions.Add(new XmlQualifiedName(prefix, "http://www.w3.org/2000/xmlns/"), nsUri);
}
public static void SetNamespace(this SyndicationItem item, string prefix, string nsUri)
{
item.AttributeExtensions.Add(new XmlQualifiedName(prefix, "http://www.w3.org/2000/xmlns/"), nsUri);
}

How to get server side variable value in xsl template?

I want use localized strings from resources in xsl template as in aspx page, like this:
<%=GetLocalizedString("grid_numberof_claim")%>. I am trying use
<xsl:text disable-output-escaping="yes">
<![CDATA[<%=GetLocalizedString("grid_numberof_claim")%>]]>
</xsl:text>
but it is not useful.
Actually i can pass localized strings inside XML node, for example "localization". But i am looking for way to get its value in aspx style.
Using ASPX style isn't possible.
You can use XsltArgumentList to send parameters to your XSLT template, as explained here: HOW TO: Execute Parameterized XSL Transformations in .NET Applications
EDIT: Yes, you can pass arguments client-side too.
xmldoc = ... // your xml document
var xslt = new ActiveXObject("Msxml2.XSLTemplate.4.0");
var xslDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.4.0");
xslDoc.async = false;
xslDoc.load("YourTemplate.xsl");
xslt.stylesheet = xslDoc;
xslProc = xslt.createProcessor();
xslProc.input = xmldoc;
xslProc.addParameter("param1", 123);
xslProc.addParameter("param2", "abc");
xslProc.transform();
But client-side leads to another solution: You can rename your XSLT file to ASPX and to use <%= %> syntax

Resources