views:

350

answers:

5

I have an XML file that I need to apply a namespace to at runtime. I’ve searched the net and most examples seem to suggest using the “SetAttributeValue” function. When I use the code below it throws an exception when I try to “ToString()” with the following error:

The prefix '' cannot be redefined from '' to 'http://schemas.datacontract.org/2004/07/' within the same start element tag.

Any ideas what is going wrong?

XDocument data = XDocument.Parse("<Root><Stuff>Test</Stuff></Root>");

string ns = "http://schemas.datacontract.org/2004/07/";

data.Root.SetAttributeValue("xmlns",ns);

SaveFile(data.ToString());
A: 

You can't do that. Namespaces are fundamental to the DOM. The only way I can think of right now is to add it using String.Replace on the entire XML text and then reparse the result.

Per Erik Stendahl
+1  A: 

Do an XSLT transformation:

<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns="your.target.default.namespace"
>
  <!-- 
    the "identity template" copies everything verbatim 
  -->
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*" />
    </xsl:copy>
  </xsl:template>

  <!-- 
    this template creates new elements that 
    are in "your.target.default.namespace"
    by default
  -->
  <xsl:template match="*">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates select="node() | @*" />
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

The above takes a namespace-free input document, and copies it. Only element nodes are re-created (by the second template).

Re-creation of element nodes causes them to be in the namespace the XSLT stylesheet is in, here it is "your.target.default.namespace".

With your example, I would get:

<Root xmlns="http://schemas.datacontract.org/2004/07/"&gt;
  <Stuff>Test</Stuff>
</Root>
Tomalak
A: 

Those attributes were also bugging me some time ago. I solved it by using the XNamespace class while creating a simple podcast. Following is a sample code from my app:

XDocument xmlDocument = new XDocument(
                new XDeclaration("1.0", "utf-8", "yes")
                );

XNamespace itunesNamespace = "http://www.itunes.com/dtds/podcast-1.0.dtd";
XElement xmlRssElement = new XElement("rss", new XAttribute(XNamespace.Xmlns + "itunes", itunesNamespace), new XAttribute("version", "2.0"));

XElement xmlChannelElement = new XElement("channel",
                             new XElement("title", "title"), 
                             new XElement(itunesNamespace + "explicit", "No"));

xmlRssElement.Add(xmlChannelElement);
xmlDocument.Add(xmlRssElement);
xmlDocument.Save(@"sample-" + DateTime.Now.Ticks + ".xml");

Note that some code has been removed for clarification

You can get your original document by using XDocument.Parse() and from there add the namespace dynamically like is done in my code before saving the document

armannvg
+1  A: 

The default namespace is actually intrinsic to the XElement and it won't let you redefine it by manipulating the attributes. The namespace for an element is exposed as part of it's XName. So to change the namespace of an element you have to rename it.

XDocument data = XDocument.Parse("<Root><Stuff>Test</Stuff></Root>");

XNamespace ns = "http://schemas.datacontract.org/2004/07/";

data.Root.Name = ns + data.Root.Name.LocalName;
Mike
+1  A: 

A little late to the party...

XNamespace ns = "http://schemas.datacontract.org/2004/07/"; 
XDocument xdoc = XDocument.Parse("<Root><Stuff>Test</Stuff></Root>");
foreach (var node in xdoc.Descendants()) { node.Name = ns + node.Name.LocalName; }
xdoc.Dump();

<Root xmlns="http://schemas.datacontract.org/2004/07/"&gt;
  <Stuff>Test</Stuff>
</Root> 
Rusty