展平 XML 文档

发布于 2025-01-04 01:37:18 字数 1812 浏览 0 评论 0原文

我目前正在尝试用 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>[email protected]</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="[email protected]" />
</members>

我可以解析元素名称及其属性,但由于此 XML 来自我无法控制的 Web 服务,因此我必须创建一些某种动态解析器​​可以将其扁平化,因为结构可以在某个时刻发生变化。

值得注意的是,XML 结构作为来自 Web 服务的 XElement。

以前有人尝试过这样做吗?分享一下方法会有所帮助吗? :-) 我们将不胜感激!

预先非常感谢。

祝一切顺利,

I am currently trying to flatten a deep-structured XML document in C# so that every value of an element is converted to an attibute.

The XML structure is as follows:

<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>[email protected]</email>
            </contact>
        </contacts>
    </member>
</members>

What I want it to look like is this:

<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="[email protected]" />
</members>

I could just parse away on the element names and their attributes, but since this XML comes from a webservice that I can't control, I have to create some sort of dynamic parser to flatten this as the structure can change at some point.

Should be worth noting that the XML structure comes as an XElement from the webservice.

Has anyone tried to do this before and would be helpful to share how? :-) It would be greatly appreciated!

Thanks a lot in advance.

All the best,

Bo

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

如痴如狂 2025-01-11 01:37:18

试试这个:

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="[email protected]" xmlns="mynamespace" />
</members>

Try this:

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))));

Result:

<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="[email protected]" xmlns="mynamespace" />
</members>
与酒说心事 2025-01-11 01:37:18

您可以使用此 XSLT 1.0 样式表。您可能想要修改它处理多个 元素的方式。

输入 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>[email protected]</email>
      </contact>
      <contact contactId="2">
        <contactPerson>second contact person</contactPerson>
        <phone/>
        <mobile>second mobile number</mobile>
        <email>second [email protected]</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="[email protected]"
           contactId2="2"
           contactPerson2="second contact person"
           mobile2="second mobile number"
           email2="second [email protected]"/>
</members>

You could use this XSLT 1.0 stylesheet. You might want to modify how it handles multiple <contact> elements.

Input 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>[email protected]</email>
      </contact>
      <contact contactId="2">
        <contactPerson>second contact person</contactPerson>
        <phone/>
        <mobile>second mobile number</mobile>
        <email>second [email protected]</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 Output

<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="[email protected]"
           contactId2="2"
           contactPerson2="second contact person"
           mobile2="second mobile number"
           email2="second [email protected]"/>
</members>
以往的大感动 2025-01-11 01:37:18

我认为 dtb 答案是最好的方法。但是,您必须注意一个重要问题。尝试添加其他联系信息,dtb 代码会崩溃。因为一名会员可以有多个联系信息,但不能有重复的属性。为了解决这个问题,我更新了代码以仅选择不同的属性。为此,我实现了 IEqualityComparer
更新后的 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 比较器重载的 Distinct 调用(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(); 
        }
    }

I think dtb answer is the best way to do it. However, you have to note one important issue. Try to add another contact information and dtb code would crash. Because a member can have more than one contact information but yet can not have duplicate attributes. In order to work around that I updated the code to select only distinct attributes. To do that I implemented IEqualityComparer<XAttribute>.
The updated linq expression would look like this

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())
                                            )));

As you can notice a Distinct call was added with a custom Equality comparer overload(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(); 
        }
    }
山人契 2025-01-11 01:37:18

您可以编写 XSLT 转换来将元素转换为属性。

You could write an XSLT transform to convert the elements to attributes.

戈亓 2025-01-11 01:37:18

您这样做是为了创建另一个 XML 文档,还是只是为了使处理更简单?如果是前者,那么当您遇到叶节点时,只需将所有值放入映射中即可。实际上,您可以迭代映射中的键值对,以仅使用属性重建 xml 标签。

Are you doing this to create another XML document, or is just to make your processing simpler? If former is the case, then you just have to put all values in a map when you come across a leaf node and that's it. You can actually then iterate over the key-value pairs in the map to reconstruct an xml tag with just attributes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文