如何防止.NET的XmlDocument输出中出现空xmlns属性?

128

在.NET中从XmlDocument生成XML时,第一次插入没有关联命名空间的元素时会出现一个空的xmlns属性;如何避免这种情况?

示例:

XmlDocument xml = new XmlDocument();
xml.AppendChild(xml.CreateElement("root",
    "whatever:name-space-1.0"));
xml.DocumentElement.AppendChild(xml.CreateElement("loner"));
Console.WriteLine(xml.OuterXml);

输出:

<root xmlns="whatever:name-space-1.0"><loner xmlns="" /></root>

期望输出:

<root xmlns="whatever:name-space-1.0"><loner /></root>

是否有适用于 XmlDocument 代码的解决方案,而不是在使用 OuterXml 将文档转换为字符串后再进行操作?

我这样做的原因是想看看是否可以使用 XmlDocument 生成的 XML 来匹配特定协议的标准 XML。空白的 xmlns 属性可能不会破坏或混淆解析器,但在我看过的这个协议的任何用法中都不存在。

7个回答

117

感谢Jeremy Lew的回答以及更多的尝试,我找到了如何删除空白的xmlns属性:在创建一个任意不想要前缀的子节点时,传入根节点的命名空间即可。在根节点处使用没有前缀的命名空间意味着你需要在子元素中使用相同的命名空间,才能使它们同样没有前缀。

修复后的代码:

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

感谢大家的回答,这些回答让我找到了正确的方向!


1
没错。将<loner>元素放在“whatever:name-space-1.0”命名空间中意味着在序列化时不会向其中添加空xmlns属性(将其放入无命名空间)。如果您需要了解命名空间的工作原理,请查看http://www.jclark.com/xml/xmlns.htm。 - JeniT
3
注意:元素需要这个(或者也许更好的是使用 doc.DocumentElement.NamespaceURI),但是如果你在 CreateAttribute() 中没有指定命名空间,你会得到 xmlns:psomething,即使它是相同的 URI。 - Jason Kleban

98

这是 JeniT 答案的变体(顺便说一句,非常感谢!)

XmlElement new_element = doc.CreateElement("Foo", doc.DocumentElement.NamespaceURI);

这样就不必在每个地方复制或重复命名空间了。


5
我认为最佳答复是:我们不必知道文档默认命名空间(在读取和修改场景中使用时很有用,即我们不是从头创建XML文件)。 - MuiBienCarlota

12

如果你的示例XML中的 <loner> 元素没有默认命名空间声明 xmlns,那么它将位于 whatever:name-space-1.0 命名空间,而不是没有命名空间。如果这正是你想要的,你需要在该命名空间中创建元素:

xml.CreateElement("loner", "whatever:name-space-1.0")
如果您希望<loner>元素不属于任何命名空间,那么生成的XML文档恰好符合您的需求,您不用担心xmlns属性是自动添加的。

3
问题出在不符合规范的XML解析器上(通常来自微软),它们无法处理xmnls =“”)。 The problem lies with non-compliant XML parsers (typically from Microsoft) that can't cope with xmnls=""). - Craig Trader
2
/ 被调用了。他们想要他们的随机 MS 嘲讽评论回来。 - user1228
@W. Craig Trader - 我还没有遇到过这样的问题。能给个例子吗? - Kev
1
正确,我不希望<loner />节点具有命名空间,但我也不希望它具有空的命名空间属性(xmlns)。我的理由只是为了看看是否可以匹配设置为此的特定协议的XML输出。 - Neil C. Obremski
5
这并不是随意攻击。微软更新应用程序块使用XML清单来确定向客户端提供什么内容。不幸的是,清单解析器无法处理xmlns="";因此,我不得不编写一个后处理程序来删除空xmlns属性。 - Craig Trader

7

由于根元素处于未加前缀的命名空间中,任何想要无命名空间的根元素子节点都必须按照您示例中的方式输出。解决方案是给根元素添加前缀,例如:

<w:root xmlns:w="whatever:name-space-1.0">
   <loner/>
</w:root>

代码:

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

谢谢,但在实际根节点中添加命名空间会与我正在处理的特定协议相关的XML文件出现问题。 - Neil C. Obremski
啊!我意识到你说的更多内容,并在撰写自己的答案时加以考虑。谢谢Jeremy。 - Neil C. Obremski

0
我通过使用工厂模式已经解决了这个问题。我为XElement对象创建了一个工厂。作为工厂实例化的参数,我指定了一个XNamespace对象。因此,每次通过工厂创建XElement时,命名空间都会自动添加。以下是工厂的代码:
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);
    }
}

1
OP 询问的是 XmlDocument,而不是 XDocument - John Saunders

0

是的,您可以防止XmlElement中的XMLNS。首先创建时间就像这样

<trkpt lat="30.53597" lon="-97.753324" xmlns="">
    <ele>249.118774</ele>
    <time>2006-05-05T14:34:44Z</time>
</trkpt>

修改代码:并传递XML命名空间,如下所示

C# 代码:

XmlElement bookElement = xdoc.CreateElement("trkpt", "http://www.topografix.com/GPX/1/1");
bookElement.SetAttribute("lat", "30.53597");
bookElement.SetAttribute("lon", "97.753324");

0
如果可能的话,创建一个序列化类,然后执行以下操作:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer serializer = new XmlSerializer(yourType);
serializer.Serialize(xmlTextWriter, someObject, ns);

这样更安全,如果你真的需要更多的控制,你可以使用属性来控制命名空间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接