XML adding child element to existing XML element - c#

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

Related

Creating namespace for XML element

I have problem with adding new node (with namespace) to my xml document which is used to generating xaml. I'm doing it like this:
XmlElement richTextColumns = xmlDoc.CreateElement("local2:RichTextColumns");
but i receive error 0xC00CE01D (while calling xmlDoc.getxml). I have tried adding attribute xmlns:local2="using:App2.Common" to xmlDoc:
var att = xmlDoc.CreateAttribute("xmlns:local2");
att.InnerText = "using:Dictionary.Common";
xmlDoc.Attributes.SetNamedItem(att);
it results in this error
Object reference not set to an instance of an object.
Thank you in advance :)
If you'd like to create an element with a specific namespace use this call:
xmlDoc.CreateElementNS("using:Dictionary.Common", "local2:elementName")
According to http://msdn.microsoft.com/en-us/library/aa335908(v=vs.71), the CreateAttribute method with a single parameter does not set the namespace, but the name of the element. Try to use one of the other permutations of this method.
You can create an element as you would typically do and then load the document back and add the namespace atribute you are looking to add.
XmlDocument doc = new XmlDocument();
    doc.LoadXml("link to yuor xml");
XNamespace xmlns = "local2";
public static void SetDefaultXmlNamespace(XElement xelem, XNamespace xmlns)
{
foreach(var e in xelem.Elements())
e.SetDefaultXmlNamespace(xmlns);
}
doc.Root.SetDefaultXmlNamespace("local2")

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

Extract contents from XML file

I have a C# application that uses a button to generate a file. Currently, I want to use C# to extract out contents from the XML file and pass it as a string. For example in my XML file, I have a tag name. I want to use c# to extract the name from the XML file. How should I go about achieving it? Below is the sample code I have currently. The entire process must be carried out using a button click.
private void button1_Click(object sender, EventArgs e)
{
XElement xml = XElement.Load("C:\\Windows 7.xml");
IEnumerable<XElement> propertyIDs = xml.Descendants("PropertyId");
foreach (XElement child in xml.Elements())
{
XElement row = child.Element("my:VM_Name");
string test = xml.ToString();
Console.WriteLine(test);
}
}
Please access this link to view my xml file: http://pastebin.com/NKhBb4Zh
I rewrote your example and changed it to make use of the XmlDocument class. As there is the my Namespace I had to add a NameSpaceManager. using this you may even select a spefic node.
string url = #"e:\temp\data.xml";
XmlDocument doc = new System.Xml.XmlDocument();
doc.Load(url);
XmlElement docElement = doc.DocumentElement;
/// loop through all childNodes
foreach (XmlNode childNode in docElement.ChildNodes)
{
Console.WriteLine(childNode.Name + ": " + childNode.InnerText);
}
XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);
mgr.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2011-05-27T03:57:48");
/// use the given XmlNamespaceManager to select a specific element
XmlNode node = docElement.SelectSingleNode("my:VM_DiskSize", mgr);
/// use innerText for node text and value for attributes only
Console.WriteLine("\n" + node.Name + ": " + node.InnerText);
hth
The comments you added to your question were very helpful. In particular:
I added this code:
XElement name = xml.Element("my:VM_Name");
string test = xml.ToString();
Console.WriteLine(test);
But I am still unable to extract out Windows 7 from the XML tag
And:
i get this error The ':' character, hexadecimal value 0x3A, cannot be included in a name.
Let's start with the error first. You cannot pass to the Element method an ns:name pair as you've done. With this API, the namespace (ns) must be supplied programatically via the XName type. So instead, that line should read:
XElement name = xml.Element(XName.Get("VM_Name", "my"));
Here we pass the qualified name as an actual XName and not as a colon-delimited string as it originates. Pay attention to the order; the namespace comes second using this syntax.
Now, once you have done all this, the other line in which you have a problem is:
string test = xml.ToString();
Here, xml refers to your root XML node whereas what you actually want is, presumably, the element for which you just queried: xml.Element(XName.Get("VM_Name", "my")). Furthermore, to get the text contents of that node, you should use the Value property. I suspect what you really want is:
string test = name.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

Categories