Уплощение XML-документа - PullRequest
       1

Уплощение XML-документа

1 голос
/ 09 февраля 2012

В настоящее время я пытаюсь сгладить глубоко структурированный XML-документ в C #, чтобы каждое значение элемента было преобразовано в attibute.

Структура 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 поступает из веб-службы, которой я не могу управлять, янеобходимо создать какой-то динамический синтаксический анализатор, чтобы сгладить это, поскольку структура может измениться в какой-то момент.

Стоит отметить, что структура XML поставляется в виде XElement из веб-службы.

Кто-нибудь пытался сделать это раньше и было бы полезно поделиться как?:-) Было бы очень признательно!

Заранее большое спасибо.

Всего наилучшего,

Бо

Ответы [ 5 ]

3 голосов
/ 09 февраля 2012

Попробуйте это:

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 голосов
/ 09 февраля 2012

Я думаю, что ответ 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)

    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(); 
        }
    }
1 голос
/ 09 февраля 2012

Вы можете использовать эту таблицу стилей 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>
1 голос
/ 09 февраля 2012

Вы можете написать XSLT-преобразование для преобразования элементов в атрибуты.

0 голосов
/ 09 февраля 2012

Вы делаете это для создания другого XML-документа или просто для упрощения обработки?Если в первом случае это так, то вам просто нужно поместить все значения в карту, когда вы сталкиваетесь с листовым узлом, и все.Затем вы можете перебрать пары ключ-значение на карте, чтобы восстановить тег xml только с атрибутами.

...