展平 XML 文档
Posted
技术标签:
【中文标题】展平 XML 文档【英文标题】:Flattening an XML document 【发布时间】:2012-03-02 04:25:00 【问题描述】:我目前正在尝试在 C# 中展平深度结构化的 XML 文档,以便将元素的每个值都转换为属性。
XML结构如下:
<members>
<member xmlns="mynamespace" id="1" status="1">
<sensitiveData>
<notes/>
<url>someurl</url>
<altUrl/>
<date1>somedate</date1>
<date2>someotherdate</date2>
<description>some description</description>
<tags/>
<category>some category</category>
</sensitiveData>
<contacts>
<contact contactId="1">
<contactPerson>some contact person</contactPerson>
<phone/>
<mobile>mobile number</mobile>
<email>some@email.com</email>
</contact>
</contacts>
</member>
</members>
我希望它看起来像这样:
<members>
<member xmlns="mynamespace" id="1" status="1" notes="" url="someurl" altUrl="" date1="somedate" date2="someotherdate" description="some description" tags="" category="some category" contactId="1" contactPerson="some contact person" phone="" mobile="mobile number" email="some@email.com" />
</members>
我可以只解析元素名称及其属性,但由于这个 XML 来自我无法控制的 web 服务,我必须创建某种动态解析器来将其展平,因为结构 可以 在某个时候改变。
值得注意的是,XML 结构来自 web 服务的 XElement。
以前有没有人尝试过这样做,并且可以帮助分享一下方法? :-) 将不胜感激!
非常感谢。
一切顺利,
博
【问题讨论】:
您显示的 XML 无效(/kontakter
没有开始标签?)...至于您的问题,没有一般答案,因为这完全取决于您要应用的规则(例如如果有多个contact
等会发生什么)。我的问题是:你为什么要“扁平化”这个 XML?
嗨 Yahia,谢谢 - 这只是一个错字 :) 我需要将其展平才能将其导入 CMS。
那么恕我直言,XSLT 是可行的方法,因为它允许在部署后更改转换规则...
【参考方案1】:
试试这个:
var doc = XDocument.Parse(@"<members>...</members>");
var result = new XDocument(
new XElement(doc.Root.Name,
from x in doc.Root.Elements()
select new XElement(x.Name,
from y in x.Descendants()
where !y.HasElements
select new XAttribute(y.Name.LocalName, y.Value))));
结果:
<members>
<member notes="" url="someurl" altUrl="" date1="somedate" date2="someotherdate" description="some description" tags="" category="some category" contactPerson="some contact person" phone="" mobile="mobile number" email="some@email.com" xmlns="mynamespace" />
</members>
【讨论】:
【参考方案2】:您可以使用这个 XSLT 1.0 样式表。您可能想要修改它处理多个 <contact>
元素的方式。
输入 XML
<members>
<member xmlns="mynamespace" id="1" status="1">
<sensitiveData>
<notes/>
<url>someurl</url>
<altUrl/>
<date1>somedate</date1>
<date2>someotherdate</date2>
<description>some description</description>
<tags/>
<category>some category</category>
</sensitiveData>
<contacts>
<contact contactId="1">
<contactPerson>some contact person</contactPerson>
<phone/>
<mobile>mobile number</mobile>
<email>some@email.com</email>
</contact>
<contact contactId="2">
<contactPerson>second contact person</contactPerson>
<phone/>
<mobile>second mobile number</mobile>
<email>second some@email.com</email>
</contact>
</contacts>
</member>
</members>
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:my="mynamespace" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:apply-templates select="node()|@*"/>
</xsl:template>
<xsl:template match="members|my:member">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()[text()][ancestor::my:member]|@*[ancestor::my:member]">
<xsl:variable name="vContact">
<xsl:if test="ancestor-or-self::my:contact">
<xsl:value-of select="count(ancestor-or-self::my:contact/preceding-sibling::my:contact) + 1"/>
</xsl:if>
</xsl:variable>
<xsl:attribute name="name()$vContact">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
XML 输出
<members>
<member xmlns="mynamespace" id="1" status="1" url="someurl" date1="somedate"
date2="someotherdate"
description="some description"
category="some category"
contactId1="1"
contactPerson1="some contact person"
mobile1="mobile number"
email1="some@email.com"
contactId2="2"
contactPerson2="second contact person"
mobile2="second mobile number"
email2="second some@email.com"/>
</members>
【讨论】:
【参考方案3】:我认为 dtb 答案是最好的方法。但是,您必须注意一个重要问题。尝试添加另一个联系信息,dtb 代码会崩溃。因为一个成员可以有多个联系信息,但不能有重复的属性。为了解决这个问题,我更新了代码以仅选择不同的属性。为此,我实施了IEqualityComparer<XAttribute>
。
更新后的 linq 表达式如下所示
var result = new XDocument(new XElement(doc.Root.Name,
from x in doc.Root.Elements()
select new XElement(x.Name, (from y in x.Descendants()
where !y.HasElements
select new XAttribute(y.Name.LocalName, y.Value)).Distinct(new XAttributeEqualityComparer())
)));
如您所见,添加了一个带有自定义平等比较器重载(XAttributeEqualityComparer)
的 Distinct 调用
class XAttributeEqualityComparer : IEqualityComparer<XAttribute>
public bool Equals(XAttribute x, XAttribute y)
return x.Name == y.Name;
public int GetHashCode(XAttribute obj)
return obj.Name.GetHashCode();
【讨论】:
【参考方案4】:您可以编写一个 XSLT 转换来将元素转换为属性。
【讨论】:
【参考方案5】:您这样做是为了创建另一个 XML 文档,还是只是为了让您的处理更简单?如果是前者,那么当您遇到叶节点时,您只需将所有值放入映射中即可。然后,您实际上可以遍历映射中的键值对以仅使用属性重构 xml 标记。
【讨论】:
以上是关于展平 XML 文档的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Java 手动展平 Elasticsearch 嵌套的 JSON 文档?