Incomplete XML attribute - c#

I am creating XML out of Dataset by dataset.GetXML() method.
I want to add attributes to it
XmlAttribute attr = xmlObj.CreateAttribute("xmlns:xsi");
attr.Value = "http://www.createattribute.com";
xmlObj.DocumentElement.Attributes.Append(attr);
attr = xmlObj.CreateAttribute("xsi:schemaLocation");
attr.Value = "http://www.createattribute.com/schema.xsd";
xmlObj.DocumentElement.Attributes.Append(attr);
xmlObj.DocumentElement.Attributes.Append(attr);
But when I open the XML file, I found "xsi:" was not there in the attribute for schemaLocation
<root xmlns="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsi="http://www.createattribute.com"
schemaLocation="http://www.createattribute.com/schema.xsd">
I want the attribute like
xsi:schemaLocation="http://www.createattribute.com/schema.xsd"
Is this always like this, or i m missing something here.
I am curious if anyone could help me if this could be resolved or give me some URL when I can find the solution for this
Thanks

The key here is that you need to tell the XmlWriter what namespaces to use and from there it will apply the correct prefixes.
In the code below the second parameter in the SetAttribute method is the namespace uri specified for xmlns:xsi namespace. This lets the XmlWrite put in the right prefix.
XmlDocument xmlObj = new XmlDocument();
xmlObj.LoadXml("<root></root>");
XmlElement e = xmlObj.DocumentElement;
e.SetAttribute("xmlns:xsi", "http://www.createattribute.com");
e.SetAttribute("schemaLocation", "http://www.createattribute.com", "http://www.createattribute.com/schema.xsd");
Similar code using the syntax from your original question is:
XmlDocument xmlObj = new XmlDocument();
xmlObj.LoadXml("<root></root>");
XmlAttribute attr = xmlObj.CreateAttribute("xmlns:xsi");
attr.Value = "http://www.createattribute.com";
xmlObj.DocumentElement.Attributes.Append(attr);
attr = xmlObj.CreateAttribute("schemaLocation", "http://www.createattribute.com");
attr.Value = "http://www.createattribute.com/schema.xsd";
xmlObj.DocumentElement.Attributes.Append(attr);

You need to specify the prefix separately, not as part of the name. There is no overload that takes just a prefix and the name, so you have to use the overload that also takes a namespace, and use null for the namespace:
attr = xmlObj.CreateAttribute("xsi", "schemaLocation", null);

Related

Reading XML Yields no Results [duplicate]

I've got an XML document with a default namespace. I'm using a XPathNavigator to select a set of nodes using Xpath as follows:
XmlElement myXML = ...;
XPathNavigator navigator = myXML.CreateNavigator();
XPathNodeIterator result = navigator.Select("/outerelement/innerelement");
I am not getting any results back: I'm assuming this is because I am not specifying the namespace. How can I include the namespace in my select?
First - you don't need a navigator; SelectNodes / SelectSingleNode should suffice.
You may, however, need a namespace-manager - for example:
XmlElement el = ...; //TODO
XmlNamespaceManager nsmgr = new XmlNamespaceManager(
el.OwnerDocument.NameTable);
nsmgr.AddNamespace("x", el.OwnerDocument.DocumentElement.NamespaceURI);
var nodes = el.SelectNodes(#"/x:outerelement/x:innerelement", nsmgr);
You might want to try an XPath Visualizer tool to help you through.
XPathVisualizer is free, easy to use.
IMPORTANT: If you are using Windows 7/8 and don't see File, Edit and Help Menu items, please press ALT key.
For anyone looking for a quick hack solution, especially in those cases where you know the XML and don't need to worry about namespaces and all that, you can get around this annoying little "feature" by simply reading the file to a string and replacing the offensive attribute:
XmlDocument doc = new XmlDocument();
string fileData = File.ReadAllText(fileName);
fileData = fileData.Replace(" xmlns=\"", " whocares=\"");
using (StringReader sr = new StringReader(fileData))
{
doc.Load(sr);
}
XmlNodeList nodeList = doc.SelectNodes("project/property");
I find this easier than all the other non-sense requiring a prefix for a default namespace when I'm dealing with a single file. Hope this helps.
When using XPath in .NET (via a navigator or SelectNodes/SelectSingleNode) on XML with namespaces you need to:
provide your own XmlNamespaceManager
and explicitly prefix all elements in XPath expression, which are in namespace.
The latter is (paraphrased from MS source linked below): because XPath 1.0 ignores default namespace specifications (xmlns="some_namespace"). So when you use element name without prefix it assumes null namespace.
That's why .NET implementation of XPath ignores namespace with prefix String.Empty in XmlNamespaceManager and allways uses null namespace.
See XmlNamespaceManager and UndefinedXsltContext don't handle default namespace for more information.
I find this "feature" very inconvenient because you cannot make old XPath namespace-aware by simply adding default namespace declaration, but that's how it works.
You can use XPath statement without using XmlNamespaceManager like this:
...
navigator.Select("//*[ local-name() = 'innerelement' and namespace-uri() = '' ]")
...
That is a simple way of selecting element within XML with default namespace definied.
The point is to use:
namespace-uri() = ''
which will found element with default namespace without using prefixes.
My answer extends the previous answer by Brandon. I used his example to create an extension method as follows:
static public class XmlDocumentExt
{
static public XmlNamespaceManager GetPopulatedNamespaceMgr(this System.Xml.XmlDocument xd)
{
XmlNamespaceManager nmsp = new XmlNamespaceManager(xd.NameTable);
XPathNavigator nav = xd.DocumentElement.CreateNavigator();
foreach (KeyValuePair<string,string> kvp in nav.GetNamespacesInScope(XmlNamespaceScope.All))
{
string sKey = kvp.Key;
if (sKey == "")
{
sKey = "default";
}
nmsp.AddNamespace(sKey, kvp.Value);
}
return nmsp;
}
}
Then in my XML parsing code, I just add a single line:
XmlDocument xdCandidate = new XmlDocument();
xdCandidate.Load(sCandidateFile);
XmlNamespaceManager nmsp = xdCandidate.GetPopulatedNamespaceMgr(); // 1-line addition
XmlElement xeScoreData = (XmlElement)xdCandidate.SelectSingleNode("default:ScoreData", nmsp);
I really like this method because it is completely dynamic in terms of loading the namespaces from the source XML file, and it doesn't completely disregard the concept of XML namespaces so this can be used with XML that requires multiple namespaces for deconfliction.
I encountered a similar problem with a blank default namespace. In this example XML, I have a mix of elements with namespace prefixes, and a single element (DataBlock) without:
<src:SRCExample xmlns="urn:some:stuff:here" xmlns:src="www.test.com/src" xmlns:a="www.test.com/a" xmlns:b="www.test.com/b">
<DataBlock>
<a:DocID>
<a:IdID>7</a:IdID>
</a:DocID>
<b:Supplimental>
<b:Data1>Value</b:Data1>
<b:Data2/>
<b:Extra1>
<b:More1>Value</b:More1>
</b:Extra1>
</b:Supplimental>
</DataBlock>
</src:SRCExample>
I attempted to use an XPath that worked in XPath Visualizer, but did not work in my code:
XmlDocument doc = new XmlDocument();
doc.Load( textBox1.Text );
XPathNavigator nav = doc.DocumentElement.CreateNavigator();
XmlNamespaceManager nsman = new XmlNamespaceManager( nav.NameTable );
foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) {
nsman.AddNamespace( nskvp.Key, nskvp.Value );
}
XPathNodeIterator nodes;
XPathExpression failingexpr = XPathExpression.Compile( "/src:SRCExample/DataBlock/a:DocID/a:IdID" );
failingexpr.SetContext( nsman );
nodes = nav.Select( failingexpr );
while ( nodes.MoveNext() ) {
string testvalue = nodes.Current.Value;
}
I narrowed it down to the "DataBlock" element of the XPath, but couldn't make it work except by simply wildcarding the DataBlock element:
XPathExpression workingexpr = XPathExpression.Compile( "/src:SRCExample/*/a:DocID/a:IdID" );
failingexpr.SetContext( nsman );
nodes = nav.Select( failingexpr );
while ( nodes.MoveNext() ) {
string testvalue = nodes.Current.Value;
}
After much headscratching and googling (which landed me here) I decided to tackle the default namespace directly in my XmlNamespaceManager loader by changing it to:
foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) {
nsman.AddNamespace( nskvp.Key, nskvp.Value );
if ( nskvp.Key == "" ) {
nsman.AddNamespace( "default", nskvp.Value );
}
}
So now "default" and "" point to the same namespace. Once I did this, the XPath "/src:SRCExample/default:DataBlock/a:DocID/a:IdID" returned my results just like I wanted. Hopefully this helps to clarify the issue for others.
In case the namespaces differ for outerelement and innerelement
XmlNamespaceManager manager = new XmlNamespaceManager(myXmlDocument.NameTable);
manager.AddNamespace("o", "namespaceforOuterElement");
manager.AddNamespace("i", "namespaceforInnerElement");
string xpath = #"/o:outerelement/i:innerelement"
// For single node value selection
XPathExpression xPathExpression = navigator.Compile(xpath );
string reportID = myXmlDocument.SelectSingleNode(xPathExpression.Expression, manager).InnerText;
// For multiple node selection
XmlNodeList myNodeList= myXmlDocument.SelectNodes(xpath, manager);
In my case adding a prefix wasn't practical. Too much of the xml or xpath were determined at runtime. Eventually I extended the methds on XmlNode. This hasn't been optimised for performance and it probably doesn't handle every case but it's working for me so far.
public static class XmlExtenders
{
public static XmlNode SelectFirstNode(this XmlNode node, string xPath)
{
const string prefix = "pfx";
XmlNamespaceManager nsmgr = GetNsmgr(node, prefix);
string prefixedPath = GetPrefixedPath(xPath, prefix);
return node.SelectSingleNode(prefixedPath, nsmgr);
}
public static XmlNodeList SelectAllNodes(this XmlNode node, string xPath)
{
const string prefix = "pfx";
XmlNamespaceManager nsmgr = GetNsmgr(node, prefix);
string prefixedPath = GetPrefixedPath(xPath, prefix);
return node.SelectNodes(prefixedPath, nsmgr);
}
public static XmlNamespaceManager GetNsmgr(XmlNode node, string prefix)
{
string namespaceUri;
XmlNameTable nameTable;
if (node is XmlDocument)
{
nameTable = ((XmlDocument) node).NameTable;
namespaceUri = ((XmlDocument) node).DocumentElement.NamespaceURI;
}
else
{
nameTable = node.OwnerDocument.NameTable;
namespaceUri = node.NamespaceURI;
}
XmlNamespaceManager nsmgr = new XmlNamespaceManager(nameTable);
nsmgr.AddNamespace(prefix, namespaceUri);
return nsmgr;
}
public static string GetPrefixedPath(string xPath, string prefix)
{
char[] validLeadCharacters = "#/".ToCharArray();
char[] quoteChars = "\'\"".ToCharArray();
List<string> pathParts = xPath.Split("/".ToCharArray()).ToList();
string result = string.Join("/",
pathParts.Select(
x =>
(string.IsNullOrEmpty(x) ||
x.IndexOfAny(validLeadCharacters) == 0 ||
(x.IndexOf(':') > 0 &&
(x.IndexOfAny(quoteChars) < 0 || x.IndexOfAny(quoteChars) > x.IndexOf(':'))))
? x
: prefix + ":" + x).ToArray());
return result;
}
}
Then in your code just use something like
XmlDocument document = new XmlDocument();
document.Load(pathToFile);
XmlNode node = document.SelectFirstNode("/rootTag/subTag");
Hope this helps
I used the hacky-but-useful approach described by SpikeDog above. It worked very well until I threw an xpath expression at it that used pipes to combine multiple paths.
So I rewrote it using regular expressions, and thought I'd share:
public string HackXPath(string xpath_, string prefix_)
{
return System.Text.RegularExpressions.Regex.Replace(xpath_, #"(^(?![A-Za-z0-9\-\.]+::)|[A-Za-z0-9\-\.]+::|[#|/|\[])(?'Expression'[A-Za-z][A-Za-z0-9\-\.]*)", x =>
{
int expressionIndex = x.Groups["Expression"].Index - x.Index;
string before = x.Value.Substring(0, expressionIndex);
string after = x.Value.Substring(expressionIndex, x.Value.Length - expressionIndex);
return String.Format("{0}{1}:{2}", before, prefix_, after);
});
}
Or, if anyone should be using an XPathDocument, like me:
XPathDocument xdoc = new XPathDocument(file);
XPathNavigator nav = xdoc.CreateNavigator();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(nav.NameTable);
nsmgr.AddNamespace("y", "http://schemas.microsoft.com/developer/msbuild/2003");
XPathNodeIterator nodeIter = nav.Select("//y:PropertyGroup", nsmgr);
1] If you have a XML file without any prefix in the namespace:
<bookstore xmlns="http://www.contoso.com/books">
…
</bookstore>
you have this workaround:
XmlTextReader reader = new XmlTextReader(#"C:\Temp\books.xml");
// ignore the namespace as there is a single default namespace:
reader.Namespaces = false;
XPathDocument document = new XPathDocument(reader);
XPathNavigator navigator = document.CreateNavigator();
XPathNodeIterator nodes = navigator.Select("//book");
2] If you have a XML file with a prefix in the namespace:
<bookstore xmlns:ns="http://www.contoso.com/books">
…
</bookstore>
Use this:
XmlTextReader reader = new XmlTextReader(#"C:\Temp\books.xml");
XPathDocument document = new XPathDocument(reader);
XPathNavigator navigator = document.CreateNavigator();
XPathNodeIterator nodes = navigator.Select("//book");
Of course, you can use a namespace manage if needed:
XmlTextReader reader = new XmlTextReader(#"C:\Temp\books.xml");
XPathDocument document = new XPathDocument(reader);
XPathNavigator navigator = document.CreateNavigator();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(reader.NameTable);
nsmgr.AddNamespace("ns", "http://www.contoso.com/book");
XPathNodeIterator nodes = navigator.Select("//book", nsmgr);
I think that it's the easiest way to make the code working in the most cases.
I hope this help to solve this Microsoft issue…
This one still keeps bugging me. I've done some testing now, so hopefully I can help you with this.
This is the source from Microsoft, which is the key to the problem
The important paragraph is here:
XPath treats the empty prefix as the null namespace. In other words, only prefixes mapped to namespaces can be used in XPath queries. This means that if you want to query against a namespace in an XML document, even if it is the default namespace, you need to define a prefix for it.
In essence, you have to remember the XPath parser uses the Namespace URI - with the design that the prefix is interchangeable. This is so, when programming, you can assign whatever prefix we want - as long as the URI matches.
For clarity with examples:
Example A:
<data xmlns:nsa="http://example.com/ns"><nsa:a>World</nsa:a></data>
This has a NULL default URI (xmlns= is not defined). Because of this /data/nsa:a returns "World".
Example B:
<data xmlns:nsa="http://example.com/ns" xmlns="https://standardns/"><nsa:a>World</nsa:a></data>
This document has a named default prefix https://standardns/. XPathNavigator.Execute with /data/nsa:a therefore returns no results. MS considers that the XML namespace uri for data should be NULL, and the namespace URI for data is actually "https://standardns/". Essentially XPath is looking for /NULL:data/nsa:a - although this won't work, as you can't refer to the NULL URI as "NULL" as a prefix. NULL prefix is the default in all XPath - hence the issue.
How do we solve this?
XmlNamespaceManager result = new XmlNamespaceManager(xDoc.NameTable);
result.AddNamespace("DEFAULT", "https://standardns/");
result.AddNamespace("nsa", "http://example.com/ns");
In this way, we can now refer to a as /DEFAULT:data/nsa:a
Example C:
<data><a xmlns="https://standardns/">World</a></data>
In this example data is in the NULL namespace. a is in the default namespace "https://standardns/". /data/a should not work, according to Microsoft, because a is in the NS https://standardns/ and data is in the namespace NULL. <a> is therefore hidden (except by doing weird "ignore the namespace" hacks) and cannot be selected upon as-is. This is essentially the root cause - you should not be able to select "a" and "data" with no prefixes for both, as this would assume that they were in the same namespace, and they aren't!
How do we solve this?
XmlNamespaceManager result = new XmlNamespaceManager(xDoc.NameTable);
result.AddNamespace("DEFAULT", "https://standardns/");
In this way, we can now refer to a as /data/DEFAULT:a as data is selected from the NULL namespace, and a is selected from the new prefix "DEFAULT". The important thing in this example is that the namespace prefix does not need to remain the same. It's perfectly acceptable to refer to a URI namespace with a different prefix in your code, as to what is written in the document you are processing.
Hope this helps some people!
In this case, it is probably namespace resolution which is the cause of the problem, but it is also possible that your XPath expression is not correct in itself. You may want to evaluate it first.
Here is the code using an XPathNavigator.
//xNav is the created XPathNavigator.
XmlNamespaceManager mgr = New XmlNamespaceManager(xNav.NameTable);
mgr.AddNamespace("prefix", "http://tempuri.org/");
XPathNodeIterator result = xNav.Select("/prefix:outerelement/prefix:innerelement", mgr);

Get schemalocation in root Node using XmlDocument using c#

i have to fill up an XML file from a DATA TABLE ,my problem is that i have to get the schemaLocation in the root node ,for this i use the code below ,then i have this result,and i dont know where is p1 coming from
In your resulting XML, p1 is a namespace. The code you have posted (in a screenshot) is defining the namespace "xsi", I'm not sure why your result is generating p1 unless you are renaming xsi somewhere that is not shown.
XmlDocument doc = new XmlDocument();
XmlDeclaration declaire = doc.CreateXmlDeclaration("1.0", "utf-8", null);
XmlElement rootnode = doc.CreateElement("BMECAT");
doc.InsertBefore(declaire, doc.DocumentElement);
doc.AppendChild(rootnode);
rootnode.SetAttribute("version", "2005");
XmlAttribute atr = doc.CreateAttribute("xsi", "schemaLocation", "http://www.w3.org/2001/XMLSchema-instance");
atr.Value = "http://www.adlnet.org/xsd/adlcp.vlp3";
rootnode.SetAttributeNode(atr);
rootnode.Attributes.Append(atr);
In your code:
XmlAttribute atr = doc.CreateAttribute("xsi", "schemaLocation", "http://www.w3.org/2001/XMLSchema-instance");
"xsi" is the name of the namespace it generates, you can control it there. This results in:
<?xml version="1.0" encoding="utf-8"?>
<BMECAT version="2005" xsi:schemaLocation="http://www.adlnet.org/xsd/adlcp.vlp3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
I'm not sure your code matches the result file you provided. When I ran the code, I get "xsi" as expected. If I set "xsi" to null there, it uses a default name, which in my case was d1p1. All instances of "xsi" were replaced with "d1p1". This makes me believe that code might be slightly different from what generated your result. I don't know where "d1p1" came from, it's likely a generated default namespace. This seems like a common default (Remove "d1p1" namespace prefix in DataContractSerializer XML output). In your provided code, if you change "xsi" to "p1" you would get your result.
I might suggest using this method instead:
How to Add schemaLocation attribute to an XML document
Here you would use the accepted answer against your XmlElement rootnode.
XmlElement.SetAttributeValue (localname, prefix, namespace, value)
Please try this code and let me know whether this helped you or not.
Particularly in this code i parse XML file and get the root element:
Then use it to select all attributes named schemaLocation. There is only one, so you can use SelectSingleNode:
The variable
schemaLocationAttribute
Contanins Value attribute through which you can get actual value.
XmlReader xmlReader = XmlReader.Create("MyXML.xml");
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(xmlReader);
XmlElement root = xmlDocument.DocumentElement;
XmlNode schemaLocationAttribute = root.SelectSingleNode("//#*[local-name()='schemaLocation']");
//Single schema value
string schemaValue = schemaLocationAttribute.Value;
//If you have multiple values in your schema
//you have to store it inside of array
string[] multipleShcemavalues = schemaLocationAttribute.Value.Split(null);
//And you have to choose whuickelement you want to use
string chooosendShcema = multipleShcemavalues[1]; //For example

Get nodes from xml files

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;
}

C# XML attribute being created improperly

I'm creating a node programmatically, however one of the attributes comes out differently than what I specified in the code:
XmlNode xResource = docXMLFile.CreateNode(XmlNodeType.Element, "resource", docXMLFile.DocumentElement.NamespaceURI);
XmlAttribute xRefIdentifier = docXMLFile.CreateAttribute("identifier");
XmlAttribute xRefADLCP = docXMLFile.CreateAttribute("adlcp:scormtype");
XmlAttribute xRefHREF = docXMLFile.CreateAttribute("href");
XmlAttribute xRefType = docXMLFile.CreateAttribute("type");
xRefIdentifier.Value = "RES-" + strRes;
xRefADLCP.Value = "sco";
xRefHREF.Value = dataRow["launch_url"].ToString().ToLower();
xRefType.Value = "webcontent";
xResource.Attributes.Append(xRefIdentifier);
xResource.Attributes.Append(xRefADLCP);
xResource.Attributes.Append(xRefHREF);
xResource.Attributes.Append(xRefType);
This ends up creating a line like the following. Note that 'adlcp:scormtype' has morphed into 'scormtype' which is not what I specified. Any ideas how to get it to show what I put in the CreateAttribute?
<resource identifier="RES-CDA68F64B849460B93BF2840A9487358" scormtype="sco" href="start.html" type="webcontent" />
This is probably expected behavior of this override CreateAttribute combined with saving the document:
The NamespaceURI remains empty unless the prefix is a recognized built-in prefix such as xmlns. In this case NamespaceURI has a value of http://www.w3.org/2000/xmlns/.
Use another override XmlDocument.CreateAttribute to specify namespace and prefix:
XmlAttribute xRefADLCP = docXMLFile.CreateAttribute(
"adlcp","scormtype", "correct-namespace-here");
You can set the Prefix for the attribute directly, instead of trying to create the attribute inline with the prefix.
XmlAttribute xRefADLCP = docXMLFile.CreateAttribute("scormtype");
// ...
xRefADLCP.Prefix = "adlcp";
xRefADLCP.Value = "sco";

Getting single node from xml document c#

I'm trying to get channel element from this document.
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:syn="http://purl.org/rss/1.0/modules/syndication/"
xmlns:admin="http://webns.net/mvcb/"
xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
<channel rdf:about="http://developers.slashdot.org/">
<title>Slashdot: Developers</title>
<link>http://developers.slashdot.org/</link>
...
I think it's in the default namespace, which seems to be "http://purl.org/rss/1.0/" so I tried like this:
XmlNamespaceManager nsmsgr = new XmlNamespaceManager(rssDoc.NameTable);
nsmsgr.AddNamespace(String.Empty, "http://purl.org/rss/1.0/");
XmlNode root = rssDoc.DocumentElement;
XmlNode channel = rssDoc.SelectSingleNode("channel", nsmsgr);
I doesn't work. XmlNode channel stays null.
You can't add it as Empty.
http://msdn.microsoft.com/en-us/library/system.xml.xmlnamespacemanager.addnamespace.aspx
The prefix to associate with the
namespace being added. Use
String.Empty to add a default
namespace. Note If the
XmlNamespaceManager will be used for
resolving namespaces in an XML Path
Language (XPath) expression, a prefix
must be specified. If an XPath
expression does not include a prefix,
it is assumed that the namespace
Uniform Resource Identifier (URI) is
the empty namespace. For more
information about XPath expressions
and the XmlNamespaceManager, refer to
the XmlNode.SelectNodes and
XPathExpression.SetContext methods.XPathExpression.SetContext methods.
So just add the default prefix as "default", then use "/*/default:channel".
Working code:
var nsmsgr = new XmlNamespaceManager(rssDoc.NameTable);
nsmsgr.AddNamespace("default", "http://purl.org/rss/1.0/");
var root = rssDoc.DocumentElement;
var channel = rssDoc.SelectSingleNode("/*/default:channel", nsmsgr);
The above code works, but it's got a hardcoded URI and it uses a "cheat" to avoid dealing with the root node. Here's a cleaner, more general solution:
var nsmsgr = new XmlNamespaceManager(rssDoc.NameTable);
var root = rssDoc.DocumentElement;
nsmsgr.AddNamespace("default", root.GetAttribute("xmlns"));
nsmsgr.AddNamespace("rdf", root.GetAttribute("xmlns:rdf"));
var channel = rssDoc.SelectSingleNode("/rdf:RDF/default:channel", nsmsgr);
Just do:
XmlNode channel = rssDoc.SelectSingleNode(#"//channel");
XmlElement root = rssDoc.DocumentElement;
XmlNode channel = root.SelectSingleNode("/channel");
That will get you the channel node, you can then reference, Attributes, Value, InnerXML, FirstChild etc to pull the data from that node.
*edit: should have been XmlElement instead of Node

Categories