如何用前缀替换 xmlns 命名空间属性?

Posted

技术标签:

【中文标题】如何用前缀替换 xmlns 命名空间属性?【英文标题】:How can I replace xmlns namespace attributes with prefixes? 【发布时间】:2017-07-21 20:33:44 【问题描述】:

我一直在尝试用 C# 编写一个实用程序,它接受一个 XML 文件,从标签中删除 xmlns 属性,在根标签中设置这些属性的前缀,然后在标签中使用这些前缀。

源 XML 文件:

<?xml version="1.0" encoding="utf-8"?>
<Main version="1.0" xmlns="urn:root:v1">
    <Report>
        <Title>Some Value</Title>
    </Report>
    <Content>
        <Address>
            <CountryName xmlns="urn:location:v2">Australia</CountryName>
        </Address>
    </Content>
</Main>

目标 XML 文件:

<?xml version="1.0" encoding="utf-8"?>
<root:Main version="1.0" xmlns:root="urn:root:v1" xmlns:loc="urn:location:v2">
    <root:Report>
        <root:Title>Some Value</root:Title>
    </root:Report>
    <root:Content>
        <root:Address>
            <loc:CountryName>Australia</loc:CountryName>
        </root:Address>
    </root:Content>
</root:Main>

我已经设法通过以下代码实现了其中的一部分。我已经用根前缀替换了所有没有属性的标记,并将 xmlns 属性添加到根标记,但从 CountryName 标记中删除 xmlns 属性并改用前缀并没有成功。

XDocument doc = XDocument.Load(@"C:\Temp\Source.xml");

var content = XElement.Parse(doc.ToString());

content.Attributes("xmlns").Remove();

content.Add(new XAttribute(XNamespace.Xmlns + "root", "urn:root:v1"));
content.Add(new XAttribute(XNamespace.Xmlns + "loc", "urn:location:v2"));

foreach (var node in doc.Root.Descendants().Where(n => n.Name.NamespaceName == "urn:location:v2"))

    node.Attribute("xmlns").Remove();
    node.Add(new XAttribute(XNamespace.Xmlns + "loc", "urn:location:v2"));
   

content.Save(@"C:\Temp\Target.xml");

任何帮助将不胜感激 - 谢谢!

【问题讨论】:

您尝试从 doc 的后代节点中删除属性,但将内容保存到您未更改的文件中。所以你只需要把doc.Root.Descendants()改成content.Descendants() @GeorgeAlexandria 谢谢 - 几乎完成了!我仍然想摆脱xmlns:loc="urn:location:v2"。有任何想法吗? &lt;loc:CountryName xmlns:loc="urn:location:v2"&gt;Australia&lt;/loc:CountryName&gt; 不要在循环中添加新属性。 @GeorgeAlexandria 搞定了!谢谢! 【参考方案1】:

你不是一百万英里之外。您需要做的就是删除任何现有的命名空间声明属性,然后将您想要的属性添加到根目录。其余的会处理。

var doc = XDocument.Load(@"C:\Temp\Source.xml");

doc.Descendants().Attributes().Where(x => x.IsNamespaceDeclaration).Remove();
doc.Root.Add(new XAttribute(XNamespace.Xmlns + "root", "urn:root:v1"));
doc.Root.Add(new XAttribute(XNamespace.Xmlns + "loc", "urn:location:v2"));

doc.Save(@"C:\Temp\Target.xml");

请参阅this fiddle 以获取演示。

【讨论】:

第二个命名空间的工作方式很有趣。它怎么知道 &lt;CountryName&gt; 应该使用 loc 命名空间前缀,因为您之前删除了所有这些前缀?我的 XSLT 明确针对 CountryName 节点。无论如何 +1! @Parfait 即使 declarations 被删除,模型仍然知道 CountryNamenamespace。 When writing,它在内部调用GetPrefixOfNamespace 来查看范围内的前缀,如果找到,将使用该前缀。【参考方案2】:

考虑XSLT,这是一种专门用于转换 XML 文件的语言。虽然我个人不知道也不使用 C#,但我知道它可以运行 XSLT 1.0 脚本。请参阅答案here。此外,您选择使用的 XSLT 处理器必须允许此解决方案的 document() 函数。

XSLT (另存为 .xsl 文件;注意标题中声明的命名空间)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                              xmlns:root="urn:root:v1" xmlns:local="urn:location:v2">
    <xsl:output omit-xml-declaration="no" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="node()|@*">
       <xsl:copy>
         <xsl:apply-templates select="node()|@*"/>
       </xsl:copy>
    </xsl:template>

    <xsl:template match="*">
      <xsl:element name="root:name()" namespace="urn:root:v1">
         <xsl:copy-of select="document('')/*/namespace::local"/>
         <xsl:apply-templates select="node()|@*"/>
      </xsl:element>
    </xsl:template>    

    <xsl:template match="*[local-name()='CountryName']">
      <xsl:element name="local:name()" namespace="urn:location:v2">
         <xsl:apply-templates select="node()|@*"/>
      </xsl:element>
    </xsl:template>

</xsl:stylesheet>

C# (见上面的链接)

var myXslTrans = new XslCompiledTransform(); 

myXslTrans.Load("XSLTScript.xsl"); 
myXslTrans.Transform("Input.xml", "Output.xml"); 

XML输出

<?xml version="1.0"?>
<root:Main xmlns:root="urn:root:v1" xmlns:local="urn:location:v2" version="1.0">
  <root:Report>
    <root:Title>Some Value</root:Title>
  </root:Report>
  <root:Content>
    <root:Address>
      <local:CountryName>Australia</local:CountryName>
    </root:Address>
  </root:Content>
</root:Main>

【讨论】:

以上是关于如何用前缀替换 xmlns 命名空间属性?的主要内容,如果未能解决你的问题,请参考以下文章

XML 属性未获取命名空间前缀

如何使用 lxml 在属性值中设置命名空间前缀?

Atitit xml命名空间机制

Open Graph 命名空间声明:带有 XMLNS 或 head 前缀的 HTML?

为标签 LinearLayout 找到了意外的命名空间前缀“xmlns”

如何在没有 xmlns="..." 的情况下使用 XML 命名空间前缀? (。网)