How to prevent blank xmlns attributes in output from .NET's XmlDocument? - c#

When generating XML from XmlDocument in .NET, a blank xmlns attribute appears the first time an element without an associated namespace is inserted; how can this be prevented?
Example:
XmlDocument xml = new XmlDocument();
xml.AppendChild(xml.CreateElement("root",
"whatever:name-space-1.0"));
xml.DocumentElement.AppendChild(xml.CreateElement("loner"));
Console.WriteLine(xml.OuterXml);
Output:
<root xmlns="whatever:name-space-1.0"><loner xmlns="" /></root>
Desired Output:
<root xmlns="whatever:name-space-1.0"><loner /></root>
Is there a solution applicable to the XmlDocument code, not something that occurs after converting the document to a string with OuterXml?
My reasoning for doing this is to see if I can match the standard XML of a particular protocol using XmlDocument-generated XML. The blank xmlns attribute may not break or confuse a parser, but it's also not present in any usage that I've seen of this protocol.

Thanks to Jeremy Lew's answer and a bit more playing around, I figured out how to remove blank xmlns attributes: pass in the root node's namespace when creating any child node you want not to have a prefix on. Using a namespace without a prefix at the root means that you need to use that same namespace on child elements for them to also not have prefixes.
Fixed Code:
XmlDocument xml = new XmlDocument();
xml.AppendChild(xml.CreateElement("root", "whatever:name-space-1.0"));
xml.DocumentElement.AppendChild(xml.CreateElement("loner", "whatever:name-space-1.0"));
Console.WriteLine(xml.OuterXml);
Thanks everyone to all your answers which led me in the right direction!

This is a variant of JeniT's answer (Thank you very very much btw!)
XmlElement new_element = doc.CreateElement("Foo", doc.DocumentElement.NamespaceURI);
This eliminates having to copy or repeat the namespace everywhere.

If the <loner> element in your sample XML didn't have the xmlns default namespace declaration on it, then it would be in the whatever:name-space-1.0 namespace rather than being in no namespace. If that's what you want, you need to create the element in that namespace:
xml.CreateElement("loner", "whatever:name-space-1.0")
If you want the <loner> element to be in no namespace, then the XML that's been produced is exactly what you need, and you shouldn't worry about the xmlns attribute that's been added automatically for you.

Since root is in an unprefixed namespace, any child of root that wants to be un-namespaced has to be output like your example. The solution would be to prefix the root element like so:
<w:root xmlns:w="whatever:name-space-1.0">
<loner/>
</w:root>
code:
XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement( "w", "root", "whatever:name-space-1.0" );
doc.AppendChild( root );
root.AppendChild( doc.CreateElement( "loner" ) );
Console.WriteLine(doc.OuterXml);

If possible, create a serialization class then do:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer serializer = new XmlSerializer(yourType);
serializer.Serialize(xmlTextWriter, someObject, ns);
It's safer, and you can control the namespaces with attributes if you really need more control.

I've solved the problem by using the Factory Pattern. I created a factory for XElement objects. As parameter for the instantiation of the factory I've specified a XNamespace object. So, everytime a XElement is created by the factory the namespace will be added automatically. Here is the code of the factory:
internal class XElementFactory
{
private readonly XNamespace currentNs;
public XElementFactory(XNamespace ns)
{
this.currentNs = ns;
}
internal XElement CreateXElement(String name, params object[] content)
{
return new XElement(currentNs + name, content);
}
}

Yes you can prevent the XMLNS from the XmlElement .
First Creating time it is coming : like that
<trkpt lat="30.53597" lon="-97.753324" xmlns="">
<ele>249.118774</ele>
<time>2006-05-05T14:34:44Z</time>
</trkpt>
Change the code : And pass xml namespace
like this
C# code:
XmlElement bookElement = xdoc.CreateElement("trkpt", "http://www.topografix.com/GPX/1/1");
bookElement.SetAttribute("lat", "30.53597");
bookElement.SetAttribute("lon", "97.753324");

Related

Generate XML in C# Using Specific XML Namespace

Target XML I am trying to achieve:
<Row><Data ss:Type="String">value</Data></Row>
Output I am currently getting:
<Row xmlns=""><Data Type="Number">0</Data></Row>
I am trying to create that target XML code using the System.Xml library, but the namespace prefix is not showing up when I create new elements. Snippet of the code that is generating the above output:
XmlElement eRow = xDoc.CreateElement("Row");
XmlElement eData = xDoc.CreateElement("Data");
XmlAttribute xAt = xDoc.CreateAttribute("ss", "Type", null);
xAt.Value = "Number";
eData.Attributes.Append(xAt);
eData.InnerText = "0";
eRow.AppendChild(eData);
I am trying to append this XML to a file that already exists. I have loaded the file as
XmlDocument xTemp = new XmlDocument();
xTemp.Load(templatePath);
and there are already namespaces in DocumentElement.Attributes that have already declared the prefix that I want to use: <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">. Essentially, I am trying to get the "ss" prefix to show up before "Type" like in the target I provided above. Additionally, the output is displaying xmlns="" as an attribute in the "Row" tag, something that I never added. I assume these are both issues with the namespace not being declared, but as mentioned above, it should already be declared in the original document that I loaded.
How can I generate the target XML code I want?
You have created your attribute xAt without specifying the namespace uri, which is equivalent to empty string namespace uri (see the corresponding MSDN doc here), that is certainly why you get the <Row xmlns="">
Actually you need to specify the exact namespace uri for it to work as you expect it.
Let me illustrate using the namespace uri you have given in your question (Illustration very similar to your initial code but might have a few small differences that you can easily modify).
String namespaceUri = "urn:schemas-microsoft-com:office:spreadsheet";
XmlDocument xDoc = new XmlDocument();
XmlElement workbook = xDoc.CreateElement("ss", "Workbook", namespaceUri);
XmlElement rows = xDoc.CreateElement("Rows");
At this step I can assume that I have an XmlDocument similar to what you have after initially loading your file. My XmlDocument has the workbook node as its DocumentElement, it uses the given prefix and namespace uri.
Now we can create the attribute:
var attribute = xDoc.CreateAttribute("ss", "Type", "urn:schemas-microsoft-com:office:spreadsheet");
attribute.Value = "String";
The namespace uri should be specified correctly otherwise it won't be correctly rendered. When this attribute is used, since the namespace it is refering to is found on the nesting element (workbook), it is not necessary to mention it again here, and the framework will automatically remove the reference to the namespace uri.
Now we can go ahead and create the Row and data elements and add the attribute to the collection of attributes of the Data element.
XmlElement eRow = xDoc.CreateElement("Row");
XmlElement eData = xDoc.CreateElement("Data");
eData.Attributes.Append(attribute);
eData.InnerText = "value";
eRow.AppendChild(eData);
rows.AppendChild(eRow);
workbook.AppendChild(rows);
xDoc.AppendChild(workbook);
We can then display the document, for example with:
Console.WriteLine(xDoc.OuterXml);
Result:
<ss:Workbook xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"><Rows><Row><Data ss:Type="String">value</Data></Row></Rows></ss:Workbook>
I hope this helps.

How do you refer to an xml node that has two namespace definitions?

I have an xml message from a 3rd party that has a node:
<ClinicalDocument xmlns="urn:hl7-org:v3" xmlns:npfitlc="NPFIT:HL7:Localisation"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
classCode="DOCCLIN" moodCode="EVN">
I created a Namespace object to use to identify npfitlc items under this node:
ns.AddNamespace("npfitlc", "NPFIT:HL7:Localisation");
But when I try to choose the ClinicalDocument node it can't find it:
XmlNode myNode = soapEnvelop.SelectSingleNode
("//soap:Envelope/soap:Body/itk:DistributionEnvelope/itk:payloads/itk:
payload/ClinicalDocument", ns);
As you can see in my doc there are multiple nodes to get to Clinical Document. And when I reference down to itk:payload it locates it fine:
XmlNode myNode = soapEnvelop.SelectSingleNode
("//soap:Envelope/soap:Body/itk:DistributionEnvelope/itk:
payloads/itk:payload", ns);
I took out xmlns="urn:hl7-org:v3" from the ClinicalDocument tag and then I could find it find with my SelectSingleNode call, but the system I sent the message to fails validation because that is missing.
I am not sure how to handle it where there is a "root" namespace defined in that node.
ClinicalDocument has no prefix and it has an xmlns="urn:hl7-org:v3" namespace declaration, which means that its namespace is urn:hl7-org:v3. The rest of the namespace declarations there are completely irrelevant for the purpose of selecting this particular element.
So what you need to do is...
Add that namespace to your namespace manager (using any nonempty prefix):
ns.AddNamespace("hl", "urn:hl7-org:v3");
Use that prefix in your XPath:
XmlNode myNode =
soapEnvelop.SelectSingleNode("//soap:Envelope/soap:Body" +
"/itk:DistributionEnvelope/itk:payloads" +
"/itk:payload/hl:ClinicalDocument", ns);
and that should do it.

Why does having a xmlns cause my C# program not to read XML?

I have a C# program that attempts to read the following xml, but can't read any elements:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Comments Here -->
<FileFeed
xmlns="http://www.mycompany.com/schemas/xxx/FileFeed/V1"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.somecompany.com/schemas/xxx/FileFeed/V1
FileFeed.xsd"
RecordCount = "1">
<Object>
<ID>PAMSMOKE110113xxx</ID>
<CorpID>12509</CorpID>
<AnotherID>201654702345</AnotherID>
<TimeStamp>2013-09-03</TimeStamp>
<Type>Some Type</Type>
<SIM_ID>89011704258012600767</SIM_ID>
<Code>ZZZ</Code>
<Year>2013</Year>
</Object>
</FileFeed>
With the above XML my C# program is unable to read any elements.. For instance the ID Element is always NULL.
Now if I simply remove the first xmlns from the above XML, my program can read all the elements without any issues. The problem is I have to process the XML file in the format that's given to me, and can't change the file format. My program reads the below XML just fine: Note the line xmlns="http://www.mycompany.com/schemas/xxx/FileFeed/V1" is removed.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Comments Here -->
<FileFeed
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.somecompany.com/schemas/xxx/FileFeed/V1
FileFeed.xsd"
RecordCount = "1">
<Object>
<ID>PAMSMOKE110113xxx</ID>
<CorpID>12509</CorpID>
<AnotherID>201654702345</AnotherID>
<TimeStamp>2013-09-03</TimeStamp>
<Type>Some Type</Type>
<SomeNumber>89011704258012600767</SomeNumber>
<Code>ZZZ</Code>
<Year>2013</Year>
</Object>
</FileFeed>
I realize I'm not posting any code, but just wondering what possible issue could I be having, where simply removing the xmlns line resolves everything??
Your problem is with xml namespaces
Using Linq2Xml
XNamespace ns = "http://www.mycompany.com/schemas/xxx/FileFeed/V1";
var xDoc = XDocument.Load(fname);
var id = xDoc.Root.Element(ns + "Object").Element(ns + "ID").Value;
Your root element FileFeed has a namespace attribute. This means that each element inside it also uses that namespace.
The Element method takes an XName as its argument. Usually you use a string which gets implicitly converted into an XName.
If you want to include a namespace you create an XNamespace and add the string. Since XNamespace overloads the + operator this will also result in an XName.
XDocument doc = XDocument.Load("Test.xml");
// this will be null
XElement objectElementWithoutNS = doc.Root.Element("Object");
XNamespace ns = doc.Root.GetDefaultNamespace();
XElement objectElementWithNS = doc.Root.Element(ns + "Object");
Xml namespaces are more or less like C# namespaces. Would you be able to access a class when its namespace is set or not set?
public namespace My.Company.Schemas {
public class FileFeed
vs
public class FileFeed {
They are two DISTINCT classes! The same applies to XML - by setting a namespace you make it possible to have documents with similar or even the same internal structure but they represent two disctinct documents that are not exchangeable. This is really convenient.
If you'd like to get help on why your actual reading method doesn't consider the namespace, you have to present the C# code. The general rule though is that any reading API makes is possible to set the namespace for actual reading.

Default namespace when created xml document

Is it possible to create children using XmlDocument.CreateElement() and XmlNode.AppendChild() without specifying the namespace and have it use a "default" namespace?
Currently, if I create a root node with a namespace and don't specify the namespace on the every childnode, the output xml will define a blank namespace.
Below is what is generated if I don't specify the namespace for every element I create. Is there a shortcut where I don't have to specify the namespace every time?
<root xmlns="http://example.com">
<child1 xmlns="">
<child2 />
</child1>
</root>
Code:
XmlDocument doc = new XmlDocument();
var rootNode = doc.CreateElement("root", "http://example.com");
doc.AppendChild(rootNode);
var child1Node = doc.CreateElement("child1");
rootNode.AppendChild(child1Node);
var child2Node = doc.CreateElement("child2");
child1Node.AppendChild(child2Node);
If you have create your XML document, and you specify the same namespace for each element in the hierarchy - something like this:
XmlDocument doc = new XmlDocument();
const string xmlNS = "http://www.example.com";
XmlElement root = doc.CreateElement("root", xmlNS);
doc.AppendChild(root);
XmlElement child1 = doc.CreateElement("child1", xmlNS);
root.AppendChild(child1);
child1.AppendChild(doc.CreateElement("child2", xmlNS));
doc.Save(#"D:\test.xml");
then you'll get this output file:
<root xmlns="http://www.example.com">
<child1>
<child2 />
</child1>
</root>
The namespace on the <root> node is inherited down the hierarchy, unless the child elements define something else explicitly.
If you create a new XmlElement using doc.CreateElement and you don't specify a XML namespace, then of course, that new element, will have a blank namespace and thus this will be serialized into that XML document you had.
I am not aware of any way to specify a default namespace to use whenever you're creating a new element - if you specify one, the element will use that namespace - if you don't specify one, it's the blank namespace.
If you are using .NET 3.5, I suggest using LINQ to XML, (System.Xml.Linq). Use the XDocument, XElement, and XAttribute classes.
But marc_s's answer is correct, the namespace is inherited.

Query an XmlDocument without getting a 'Namespace prefix is not defined' problem

I've got an Xml document that both defines and references some namespaces. I load it into an XmlDocument object and to the best of my knowledge I create a XmlNamespaceManager object with which to query Xpath against. Problem is I'm getting XPath exceptions that the namespace "my" is not defined. How do I get the namespace manager to see that the namespaces I am referencing are already defined. Or rather how do I get the namespace definitions from the document to the namespace manager.
Furthermore tt strikes me as strange that you have to provide a namespace manager to the document which you create from the documents nametable in the first place. Even if you need to hardcode manual namespaces why can't you add them directly to the document. Why do you always have to pass this namespace manager with every single query? What can't XmlDocument just know?
Code:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(programFiles + #"Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\HfscBookingWorkflow\template.xml");
XmlNamespaceManager ns = new XmlNamespaceManager(xmlDoc.NameTable);
XmlNode referenceNode = xmlDoc.SelectSingleNode("/my:myFields/my:ReferenceNumber", ns);
referenceNode.InnerXml = this.bookingData.ReferenceNumber;
XmlNode titleNode = xmlDoc.SelectSingleNode("/my:myFields/my:Title", ns);
titleNode.InnerXml = this.bookingData.FamilyName;
Xml:
<?xml version="1.0" encoding="UTF-8" ?>
<?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:Inspection:-myXSD-2010-01-15T18-21-55" solutionVersion="1.0.0.104" productVersion="12.0.0" PIVersion="1.0.0.0" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<my:myFields xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-01-15T18:21:55" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003">
<my:DateRequested xsi:nil="true" />
<my:DateVisited xsi:nil="true" />
<my:ReferenceNumber />
<my:FireCall>false</my:FireCall>
Update:
ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
ns.AddNamespace("xhtml", "http://www.w3.org/1999/xhtml");
ns.AddNamespace("xd", "http://schemas.microsoft.com/office/infopath/2003");
ns.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-01-15T18:21:55");
This does the job, but it mean's I have to hard code to this particular xml schema. This schema represents an infopath form template. In particular the my namespace url will be different for every form template so I really don't want to hardcode this. It would be nice to find a clean way to get this namespace from the xml without resorting to RegEx.
I was hoping that the XmlNamespaceManager would just sort of pick up the namespace definitions form the NameTable. I mean their in the Xml but I still have to define them.
ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
ns.AddNamespace("xhtml", "http://www.w3.org/1999/xhtml");
ns.AddNamespace("xd", "http://schemas.microsoft.com/office/infopath/2003");
ns.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-01-15T18:21:55");
This does the job, but it mean's I have to hard code to this particular xml schema. This schema represents an infopath form template. In particular the my namespace url will be different for every form template so I really don't want to hardcode this. It would be nice to find a clean way to get this namespace from the xml without resorting to Regex.
I was hoping that the XmlNamespaceManager would just sort of pick up the namespace definitions form the NameTable. I mean their in the Xml but I still have to define them.
Here is the answer to the "What can't XmlDocument just know?" question.
NameTable is just an optimization for storing names. It has actually nothing to do with namespaces.
And even if XmlNamespaceManager could infer all namespaces and prefixes from XML doc that won't help in general case because of XML namespaces nature, e.g. what would XmlNamespaceManager map "my" prefix in this case:
<root>
<foo xmlns:my="blah"/>
<foo xmlns:my="balh-blah-blah"/>
</root>
Have you defined "my" in the namespace-manager?
ns.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-01-15T18:21:55");
Or better - choose something that is unlikely to conflict. It does seem odd that it didn't pick it up from the name-table, though.
For me with InfoPath 2007 this solved the problem
static public XmlNamespaceManager GetNameSpaceManager(this XmlDocument document)
{
XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(document.NameTable);
xmlNamespaceManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
xmlNamespaceManager.AddNamespace("dfs", "http://schemas.microsoft.com/office/infopath/2003/dataFormSolution");
xmlNamespaceManager.AddNamespace("d", "http://schemas.microsoft.com/office/infopath/2003/ado/dataFields");
xmlNamespaceManager.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2012-03-29T06:28:28");
xmlNamespaceManager.AddNamespace("xd", "http://schemas.microsoft.com/office/infopath/2003");
return xmlNamespaceManager;
}

Categories