展开XML文档
本文关键字:文档 XML 展开 | 更新日期: 2023-09-27 18:23:51
我目前正在尝试在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。
以前有人尝试过这样做吗?分享一下会很有帮助吗?:-)非常感谢!
提前非常感谢。
一切顺利,
Bo
试试这个:
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>
您可以使用这个XSLT1.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>
我认为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())
)));
正如您所注意到的,添加了一个Distinct调用,其中包含一个自定义的Equality比较器重载(XAttributeEqualityComparer)
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();
}
}
您可以编写一个XSLT转换来将元素转换为属性。
您这样做是为了创建另一个XML文档,还是只是为了简化处理?如果是前者,那么当你遇到一个叶节点时,你只需要把所有的值都放在一个映射中,就可以了。然后你可以迭代映射中的键值对,以重建一个只有属性的xml标记。