XDocument xmlns url attributes persist to child nodes - c#

Based on this SO solution, adding the same namespace to your child node will prevent creating empty xmlns="" attributes. I'm not getting empty attributes, instead it duplicates the same xmlns in root to child node.
My current output:
<Root xmlns="http://my.namespace">
<FirstElement xmlns="http://my.namespace"/>
</Root>
Expected output:
<Root xmlns="http://my.namespace">
<FirstElement/>
</Root>
Sharing my code:
private XDocument CreateRootTag()
{
XNamespace xmlns = XNamespace.Get("http://my.namespace");
var xdec = new XDeclaration("1.0", "utf-8", "yes");
XDocument xml = new XDocument(
xdec,
new XElement(
xmlns + "Root",
new XAttribute("version", "1.0"),
CreateFirstElementTag())); // <--- adding child node containing duplicate xmlns as root
return xml;
}
private XElement CreateFirstElementTag()
{
XNamespace xmlns = XNamespace.Get("http://my.namespace");
XElement firstElementTag = new XElement(xmlns + "FirstElement","hello");
return firstElementTag;
}
How to prevent persisting xmlns="my.namespace" attributes in child node?
Please let me know if you have any questions.
Thanks.

I ran your code as follows. And didn't encounter any issues.
c#
void Main()
{
XDocument xdoc = CreateRootTag();
Console.WriteLine(xdoc);
}
private XDocument CreateRootTag()
{
XNamespace xmlns = XNamespace.Get("http://my.namespace");
var xdec = new XDeclaration("1.0", "utf-8", "yes");
XDocument xml = new XDocument(
xdec,
new XElement(
xmlns + "Root",
new XAttribute("version", "1.0"),
CreateFirstElementTag())); // <--- adding child node containing duplicate xmlns as root
return xml;
}
private XElement CreateFirstElementTag()
{
XNamespace xmlns = XNamespace.Get("http://my.namespace");
XElement firstElementTag = new XElement(xmlns + "FirstElement", "hello");
return firstElementTag;
}
Output
<Root version="1.0" xmlns="http://my.namespace">
<FirstElement>hello</FirstElement>
</Root>

Related

How to write xsd:schema tag using XmlDocument

I'm trying to write a XML-document programatically.
I need to add <xsd:schema> tag to my document.
Currently I have:
var xmlDoc = new XmlDocument();
var root = xmlDoc.CreateElement("root");
xmlDoc.AppendChild(root);
var xsdSchemaElement = xmlDoc.CreateElement("schema");
xsdSchemaElement.Prefix = "xsd";
xsdSchemaElement.SetAttribute("id", "root");
root.AppendChild(xsdSchemaElement);
However, this renders to:
<root>
<schema id="root" />
</root>
How do I get the tag to be <xsd:schema>?
Already tried var xsdSchemaElement = xmlDoc.CreateElement("xsd:schema"); which simply ignores the xsd:.
Edit #1
Added method
private static XmlSchema GetTheSchema(XmlDocument xmlDoc)
{
var schema = new XmlSchema();
schema.TargetNamespace = "xsd";
return schema;
}
which is called like xmlDoc.Schemas.Add(GetTheSchema(xmlDoc)); but does not generate anything in my target XML.
Using LINQ-to-XML, you can nest XElements and XAttributess in a certain hierarchy to construct an XML document. As for namespace prefix, you can use XNamespace.
Notice that every namespace prefix, such as xsd in your case, has to be declared before it is used, something like xmlns:xsd = "http://www.w3.org/2001/XMLSchema".
XNamespace xsd = "http://www.w3.org/2001/XMLSchema";
var doc =
new XDocument(
//root element
new XElement("root",
//namespace prefix declaration
new XAttribute(XNamespace.Xmlns+"xsd", xsd.ToString()),
//child element xsd:schema
new XElement(xsd + "schema",
//attribute id
new XAttribute("id", "root"))));
Console.WriteLine(doc.ToString());
output :
<root xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:schema id="root" />
</root>

Customizing an XML element using LINQ To XML

I have the following code:
XNamespace testNM = "urn:lst-emp:emp";
XDocument xDoc;
string path = "project_data.xml";
if (!File.Exists(path))
{
xDoc = new XDocument(
new XDeclaration("1.0", "UTF-16", null),
new XElement(testNM + "Test")
);
}
else
{
xDoc = XDocument.Load(path);
}
var element = new XElement("key",
new XElement("Type", type),
new XElement("Value", value));
xDoc.Element(testNM + "Test").Add(element);
// Save to Disk
xDoc.Save(path);
which produces an output in the XML file like this:
<?xml version="1.0" encoding="utf-16"?>
<Test xmlns="urn:lst-emp:emp">
<key xmlns="">
<Type>String</Type>
<Value>somestring</Value>
</key>
</Test>
How can I get an output like this in the XML file instead?
<?xml version="1.0" encoding="utf-16"?>
<Tests xmlns="urn:lst-emp:emp">
<key name="someString">
<Type>String</Type>
<Value>somestring</Value>
</key >
</Tests>
Note its only the 3rd line that has changed in the XML file.
You can do it this way:
var element = new XElement("key",
new XAttribute("name", "someString"),
new XElement("Type", "type"),
new XElement("Value", "value"));
To provide a complete version of Bilyukov's answer, this should produce the output expected. Obviously substitute the "someString" static string with a variable populated as you wish. Changes include using XName.Get(string, string) to create the appropriate XName objects for the XElement constructors.
XNamespace testNM = "urn:lst-emp:emp";
XDocument xDoc;
string path = "project_data.xml";
if (!File.Exists(path))
{
xDoc = new XDocument(
new XDeclaration("1.0", "UTF-16", null),
new XElement(XName.Get("Tests", testNM.NamespaceName))
);
}
else
{
xDoc = XDocument.Load(path);
}
var element = new XElement(XName.Get("key", testNM.NamespaceName),
new XAttribute("name", "someString"),
new XElement("Type", type),
new XElement("Value", value));
xDoc.Element(XName.Get("Tests", testNM.NamespaceName)).Add(element);
// Save to Disk
xDoc.Save(path);
Your XML has default namespace. Descendants of the element where default namespace declared is considered in the same default namespace, unless it is explicitly declared with different namespace. That's why you need to use the same XNamespace for <key> element. :
var element = new XElement(testNM +"key",
new XAttribute("name", "someString"),
new XElement(testNM +"Type", type),
new XElement(testNM +"Value", value));

XmlDocument from XML string that contains custom namespaces causes XmlException?

I need to create an XmlDocument partly by using old XML and partly by creating new. The problem is that the old XML contains custom namespaces and I can't seem to be able to use them as I get an XmlException. I've tried to add the namespace to many different places but I can't get over the Exception!
The Exception
System.Xml.XmlException was unhandled by user code
Message='my' is an undeclared prefix. Line 1, position 42.
Source=System.Xml
My Code
XmlDocument doc = new XmlDocument();
XmlSchema schema = new XmlSchema();
schema.Namespaces.Add("my", "http://foobar.com/");
doc.Schemas.Add(schema);
XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
doc.AppendChild(dec);
XmlElement root = doc.CreateElement("root");
root.SetAttribute("xmlns:my", "http://foobar.com/");
doc.AppendChild(root);
foreach (var item in GetItems())
{
XmlElement elem = doc.CreateElement("item");
elem.SetAttribute("id", item.id);
// Append body to elem
XmlElement body = doc.CreateElement("body");
body.InnerXml = item.Body; // Here is where I get the exception
elem.AppendChild(body);
// Append elem to root
root.AppendChild(elem);
}
Input from Item.Body is similar to
<aaa><bbb my:attr="55">Foo</bbb></aaa>
I expected the output to be similar to
<?xml version="1.0" encoding="utf-8"?>
<root my:attr="http://foobar.com/">
<item id="12345">
<body>
<aaa>
<bbb my:attr="55">Foo</bbb>
</aaa>
</body>
</item>
</root>
I'm open to alternatives to using this method. After I create the XmlDocument I prettyprint it, validate it against a schema and then push it out for the user to see.
The following is a workaround, best I can come up with:
XNamespace my = "http://foobar.com/";
var doc = new XDocument(new XElement("root",
new XAttribute(XNamespace.Xmlns + "my", my)));
var body = new XElement("body");
doc.Root.Add(new XElement("item", new XAttribute("id", 12345), body));
string innerItem = #"<aaa><bbb my:attr=""55"">Foo</bbb></aaa>";
string itemWrap = #"<wrap xmlns:my=""http://foobar.com/"">" + innerItem + "</wrap>";
XElement item = XElement.Parse(itemWrap);
body.Add(item.Element("aaa"));
Console.WriteLine(doc);

Create XML doc by LINQ, add xmlns,xmlns:xsi to it

I try to create an GPX XML document by LINQ to XML.
Everything works great, except adding xmlns, xmlns:xsi attributes to the doc. By trying it different way I get different exceptions.
My code:
XDocument xDoc = new XDocument(
new XDeclaration("1.0", "UTF-8", "no"),
new XElement("gpx",
new XAttribute("creator", "XML tester"),
new XAttribute("version","1.1"),
new XElement("wpt",
new XAttribute("lat","7.0"),
new XAttribute("lon","19.0"),
new XElement("name","test"),
new XElement("sym","Car"))
));
The output should also contain this:
xmlns="http://www.topografix.com/GPX/1/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
How can I add it by Linq to XML? I tried several ways but it does not work, exceptions during compile time.
See How to: Control Namespace Prefixes. You could use code like this:
XNamespace ns = "http://www.topografix.com/GPX/1/1";
XNamespace xsiNs = "http://www.w3.org/2001/XMLSchema-instance";
XDocument xDoc = new XDocument(
new XDeclaration("1.0", "UTF-8", "no"),
new XElement(ns + "gpx",
new XAttribute(XNamespace.Xmlns + "xsi", xsiNs),
new XAttribute(xsiNs + "schemaLocation",
"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"),
new XAttribute("creator", "XML tester"),
new XAttribute("version","1.1"),
new XElement(ns + "wpt",
new XAttribute("lat","7.0"),
new XAttribute("lon","19.0"),
new XElement(ns + "name","test"),
new XElement(ns + "sym","Car"))
));
You have to specify the namespace for each element, because that's what using xmlns this way means.
From http://www.falconwebtech.com/post/2010/06/03/Adding-schemaLocation-attribute-to-XElement-in-LINQ-to-XML.aspx:
To generate the following root node and namespaces:
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:SchemaLocation="http://www.foo.bar someSchema.xsd"
xmlns="http://www.foo.bar" >
</root>
Use the following code:
XNamespace xsi = XNamespace.Get("http://www.w3.org/2001/XMLSchema-instance");
XNamespace defaultNamespace = XNamespace.Get("http://www.foo.bar");
XElement doc = new XElement(
new XElement(defaultNamespace + "root",
new XAttribute(XNamespace.Xmlns + "xsi", xsi.NamespaceName),
new XAttribute(xsi + "schemaLocation", "http://www.foo.bar someSchema.xsd")
)
);
Be aware - if you want to add elements to the document, you need to specify the defaultNamespace in the element name or you will get xmlns="" added to your element. For example, to add a child element "count" to the above document, use:
xdoc.Add(new XElement(defaultNamespace + "count", 0)

manipulation of xml using c#

If I want to add, update or delete node in the xml using c#, how can it be done? My xml is shown below. I dont want transactionID node. I want to add a node called <Transformation>XML</Transformation> after corelationID node.
<?xml version="1.0" ?>
<GovTalkMessage xmlns="http://www.govtalk.gov.uk/CM/envelope">
<EnvelopeVersion>2.0</EnvelopeVersion>
<Header>
<MessageDetails>
<Class>HMRC-VAT-DEC</Class>
<Qualifier>poll</Qualifier>
<Function>submit</Function>
<TransactionID />
<CorrelationID>1B93D48C02D740C6B79DE68A27F3ADE5</CorrelationID>
<ResponseEndPoint PollInterval="10">https://secure.dev.gateway.gov.uk/poll</ResponseEndPoint>
<GatewayTimestamp>2011-04-05T07:41:43.018</GatewayTimestamp>
</MessageDetails>
<SenderDetails />
</Header>
<GovTalkDetails>
<Keys />
</GovTalkDetails>
<Body />
</GovTalkMessage>
The easiest thing to use would be LINQ to XML. For example:
XDocument doc = XDocument.Load("file.xml");
XNamespace ns = "http://www.govtalk.gov.uk/CM/envelope";
// Remove TransationID
XElement transactionElement = doc.Descendants(ns + "TransactionID").Single();
transactionElement.Remove();
// Add XML:
XElement correlationElement = doc.Descendants(ns + "CorrelectionID").Single();
XElement newElement = new XElement(ns + "XML");
correlationElement.AddAfterSelf(newElement);
// Save back
doc.Save("new-file.xml");
//Load the XML
XmlDocument documentXML = new XmlDocument();
documentXML.Load(Server.MapPath("AddDeleteUpdate.xml"));
XmlNamespaceManager xmlns = new XmlNamespaceManager(documentXML.NameTable);
xmlns.AddNamespace("bk", "http://www.govtalk.gov.uk/CM/envelope");
//Identify the parent node i.e <MessageDetails>
XmlNode nodeMessage = documentXML.SelectSingleNode("//bk:GovTalkMessage/bk:Header/bk:MessageDetails", xmlns);
//Delete the node.
XmlNode nodeTransactionID = documentXML.SelectSingleNode("//bk:GovTalkMessage/bk:Header/bk:MessageDetails/bk:TransactionID", xmlns);
nodeMessage.RemoveChild(nodeTransactionID);
//Create the new XML noded to be added.
XmlNode controlAttrNode = null;
controlAttrNode = documentXML.CreateElement("Transformation");
controlAttrNode.InnerText = "XML";
controlAttrNode.Attributes.RemoveAll();
//Get the node object to where it need to be added.
XmlNode nodeCorrelation = documentXML.SelectSingleNode("//bk:GovTalkMessage/bk:Header/bk:MessageDetails/bk:CorrelationID", xmlns);
//Insert the node after.
nodeMessage.InsertAfter(controlAttrNode, nodeCorrelation);
documentXML.Save(Server.MapPath("AddDeleteUpdate.xml"));
You'll need
XMLNode.InsertAfter(newChildNode,referenceChildNode)
This should kickstart you:
http://msdn.microsoft.com/en-US/library/system.xml.xmlnode.insertafter%28v=VS.80%29.aspx

Categories

Resources