C# XML attribute being created improperly - c#

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

Related

XML adding child element to existing XML element

I have the following XML element:
<ctext:RootAugmentation>
</ctext:RootAugmentation>
I would like to add the following element inside the above element:
<ctext:DetailsText>Example</ctext:DetailsText>
I have the following code:
string filename = #"C:\test.xml";
XmlDocument doc = new XmlDocument();
doc.LoadXml(File.ReadAllText(filename));
XmlNodeList elemList = doc.GetElementsByTagName("ctext:RootAugmentation");
XmlElement detailsElement = doc.CreateElement("ctext:DetailsText");
detailsElement.InnerText = "Example";
if (elemList.Count == 1)
{
for (int i = 0; i < elemList.Count; i++)
{
Console.WriteLine(elemList[i].InnerText);
elemList[i].AppendChild(detailsElement);
}
doc.Save(filename);
}
else
{
// update existing "ctext:DetailsText" value
}
I'm able to add the child element but tags are wrong:
<ctext:RootAugmentation>
<DetailsText>Example narrative</DetailsText>
</ctext:RootAugmentation>
I'd like it to go in as:
<ctext:DetailsText>Example narrative</ctext:DetailsText>
The prefix is just a way to not have to specify the namespace in every element that uses it, the namespace is most probably specified in the document the first time the prefix is used
To find the namespace URI, you can either look in the XML document and find the attribute that looks like:
<ctext:SomeElement xmlns:ctext="<namespace uri>">...
Once you have found it, you can use the CreateElement(string, string) overload to specify the namespaceUri.
doc.CreateElement("DetailsText", "<namespace uri for ctext>");
This will result in the element looking like:
<ctext:DetailsText />
You can specify the prefix in the CreateElement call i.e. "ctext:DetailsText" (Or by another overload), but this will automatically be looked up based on the namespaceUri you provide so in your case it's unnecessary. If you were to specify a different prefix, this will register the new prefix with the namespaceUri and add a new xmlns attribute on the element (which you don't want).

C# Create XmlElement from string without xmlns=""

I am building an xml document and I have declared the namespace at the very top.
<Root xmlns="http://www.omg.org/space/xtce" xmlns:xtce="http://www.omg.org/space/xtce" ...>
At some arbitrary level below I want to AppendChild with an element created from a string. My goal is to end up with a document that contains that element without the xmlns AT ALL.
This is the closest I have gotten-
string someElementStr = "<SomeElement name="foo"><SubElement name="bar" /></SomeElement>";
XmlDocumentFragment node = doc.CreateDocumentFragment();
node.InnerXml = someElementStr;
someXmlNodeWithinDoc.AppendChild(node);
This code results in-
<SomeElement name="foo" xmlns="">
<SubElement name="bar" />
</SomeElement>
in the final document.
I use a different construct when I do not have to go from a string to XML-
XmlElement elem = doc.CreateElement("SomeElement", "http://www.omg.org/space/xtce");
elem.SetAttribute("name","foo");
someXmlNodeWithinDoc.AppendChild(elem);
and this yields exactly what I want.
<SomeElement name="foo">
</SomeElement>
I would like to do something line this in my current solution
node.setNamespace("http://www.omg.org/space/xtce") then the document would omit xmlns because it is same as root.
Can someone tell me the idiomatic way to build a document with a single namespace use within, where some elements are stored in the model as a string?
This issue is almost identical to mine except the solution has the luxury of only providing the sub element as a string (everything under "new"). I need the entire element.
string xmlRoot = "<Root xmlns=\"http://www.omg.org/space/xtce\"></Root>";
string xmlChild = "<SomeElement name=\"foo\"><SubElement name = \"bar\"/></SomeElement >";
XDocument xDoc = XDocument.Parse(xmlRoot);
XDocument xChild = XDocument.Parse(xmlChild);
xChild.Root.Name = xDoc.Root.GetDefaultNamespace() + xChild.Root.Name.LocalName;
foreach (XElement xChild2 in xChild.Root.Nodes())
{
xChild2.Name = xDoc.Root.GetDefaultNamespace() + xChild2.Name.LocalName;
}
xDoc.Root.Add(xChild.Root);
string xmlFinal = xDoc.ToString();
This is the solution I ended up with. I did not use #shop350 solution because I didn't want to use XDocument,XElement... Thank you for the feedback though!
// create a fragment which I am going to build my element based on text.
XmlDocumentFragment frag = doc.CreateDocumentFragment();
// here I wrap my desired element in another element "dc" for don't care that has the namespace declaration.
string str = "";
str = "<dc xmlns=\"" + xtceNamespace + "\" ><Parameter name=\"paramA\" parameterTypeRef=\"paramAType\"><AliasSet><Alias nameSpace=\"ID\" alias=\"0001\"/></AliasSet></Parameter></dc>";
// set the xml for the fragment (same namespace as doc)
frag.InnerXml = str;
// let someXmlNodeWithinDoc be of type XmlNode which I determined based on XPath search.
// Here I attach the child element "Parameter" to the XmlNode directly effectively dropping the element <dc>
someXmlNodeWithinDoc.AppendChild(frag.FirstChild.FirstChild);

Get xml node value as string C#

I've been trying to pull the value of the XML node into a string. Here is what the XML looks like:
<currentvin value="1FTWW31R08EB18119" />
I can't seem to figure out how to grab that value. I didn't write this XML, by the way. So far I have tried several approaches, including the following:
public void xmlParse(string filePath)
{
XmlDocument xml = new XmlDocument();
xml.Load(filePath);
XmlNode currentVin = xml.SelectSingleNode("/currentvin");
string xmlVin = currentVin.Value;
Console.WriteLine(xmlVin);
}
Which doesn't work. I then tried:
public void xmlParse(string filePath)
{
XmlDocument xml = new XmlDocument();
xml.Load(filePath);
string xmlVin = xml.SelectSingleNode("/currentvin").Value;
Console.WriteLine(xmlVin);
}
But that doesn't work either. I am getting a null reference exception stating that Object reference not set to an instance of an object. Any ideas?
I think you're confusing the Value property of the XmlNode class, with an XML attribute named "value".
value is an attribute in your xml so either modify your xpath query to be
xml.SelectSingleNode("/currentvin/#value").Value
Or user the Attributes collection of the selected XmlNode.
You are looking for the value of the attribute "value" (that's a handful) not the value of the node itself - so you have to use the Attribute property:
string xmlVin = xml.SelectSingleNode("/currentvin").Attributes["value"].Value;
Or in the first version:
XmlNode currentVin = xml.SelectSingleNode("/currentvin");
string xmlVin = currentVin.Attributes["value"].Value;
If your entire XML contains only this node then it could be xml.DocumentElement.Attributes["value"].Value;

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

Incomplete XML attribute

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

Categories