如何用前缀替换 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"
。有任何想法吗? <loc:CountryName xmlns:loc="urn:location:v2">Australia</loc:CountryName>
不要在循环中添加新属性。
@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 以获取演示。
【讨论】:
第二个命名空间的工作方式很有趣。它怎么知道<CountryName>
应该使用 loc
命名空间前缀,因为您之前删除了所有这些前缀?我的 XSLT 明确针对 CountryName 节点。无论如何 +1!
@Parfait 即使 declarations 被删除,模型仍然知道 CountryName
的 namespace。 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 命名空间属性?的主要内容,如果未能解决你的问题,请参考以下文章
Open Graph 命名空间声明:带有 XMLNS 或 head 前缀的 HTML?