我目前正在尝试在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。
之前有没有人试图这样做,并有助于分享如何? :-)非常感谢!
提前多多感谢。
一切顺利,
博
答案 0 :(得分:3)
试试这个:
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>
答案 1 :(得分:2)
我认为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())
)));
您可以注意到,添加了一个带有自定义Equality比较器重载的{6}的不同调用
(XAttributeEqualityComparer)
答案 2 :(得分:1)
您可以编写一个XSLT转换来将元素转换为属性。
答案 3 :(得分:1)
您可以使用此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>
答案 4 :(得分:0)
您是否这样做是为了创建另一个XML文档,还是只是为了让您的处理更简单?如果是前者,那么当您遇到叶子节点时,您只需将所有值放在地图中即可。实际上,您可以迭代映射中的键值对,以仅使用属性重建xml标记。