Сглаживание XML-файла с использованием атрибутов ref - PullRequest
2 голосов
/ 24 ноября 2010

Я бы хотел сгладить файл данных xml (обратите внимание, что это не схема, .xsd, файл) программно с использованием C # (поэтому внешний редактор xml не будет работать, если у него нет API).Для примера древовидной структуры:

<root>
    <A>
        <B att="val">
            <C>
                someData
            </C>
        </B>
    </A>
    <A>
        <B>
             someOtherData
        </B>
        <B>
            moreData
        </B>
    </A>
</root>

Я бы хотел сгладить его до:

<root>
    <A>
        <B ref="b1" />
    </A>
    <A>
        <B ref="b2" />
        <B ref="b3" />
    </A>
    <B id="b1" att="val">
         <C ref="c1" />
    </B>
    <B id="b2">
        someOtherData
    </B>
    <B id="b3">
        moreData
    </B>
    <C id="c1">
         someData
    </C>
</root>

Есть ли способ добиться этого с помощью C #?

ИЕсть ли способ превратить плоский XML обратно в древовидную структуру?Мне бы хотелось что-то настолько универсальное, насколько это возможно, чтобы любой xml-файл мог быть сведен как таковой.

Есть такой же вопрос , поэтому , но он не имеет отношения к ссылкам.

Ответы [ 3 ]

3 голосов
/ 24 ноября 2010

Это преобразование :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="Lower" select=
  "'abcdefghijklmnopqrstuvwxyz'"
  />

 <xsl:variable name="vUpper" select=
  "'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"
  />

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/*">
  <root>
    <xsl:apply-templates select="node()"/>
    <xsl:apply-templates select="/*/*//*" mode="extract">
     <xsl:sort select="count(ancestor::*)" data-type="number"/>
    </xsl:apply-templates>
  </root>
 </xsl:template>

 <xsl:template match="*[ancestor::*[2]]">
   <xsl:variable name="vPos">
     <xsl:number level="any"/>
   </xsl:variable>

   <xsl:element name="{name()}">
     <xsl:attribute name="ref">
       <xsl:value-of select=
        "concat(translate(name(),$vUpper,$Lower),$vPos)"/>
     </xsl:attribute>
   </xsl:element>
 </xsl:template>

 <xsl:template match="*" mode="extract">
  <xsl:variable name="vPos">
   <xsl:number level="any"/>
  </xsl:variable>

  <xsl:element name="{name()}">
    <xsl:attribute name="id">
       <xsl:value-of select=
        "concat(translate(name(),$vUpper,$Lower),$vPos)"/>
    </xsl:attribute>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:element>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному документу XML :

<root>
    <A>
        <B att="val">
            <C>
                someData
            </C>
        </B>
    </A>
    <A>
        <B>
             someOtherData
        </B>
        <B>
            moreData
        </B>
    </A>
</root>

производит точно требуемый, правильный результат :

<root>
   <A>
      <B ref="b1"/>
   </A>
   <A>
      <B ref="b2"/>
      <B ref="b3"/>
   </A>
   <B id="b1" att="val">
      <C ref="c1"/>
   </B>
   <B id="b2">
             someOtherData
        </B>
   <B id="b3">
            moreData
        </B>
   <C id="c1">
                someData
            </C>
</root>

Обратное преобразование :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <xsl:key name="kElbyId" match="*" use="@id"/>

 <xsl:template match="node()|@*">
   <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
   </xsl:copy>
 </xsl:template>

 <xsl:template match="*[@ref]">
  <xsl:apply-templates mode="deepen"
       select="key('kElbyId',@ref)"/>
 </xsl:template>

 <xsl:template match="*[@id]"/>
 <xsl:template match="*[@id]" mode="deepen">
  <xsl:copy>
   <xsl:apply-templates
        select="@*[not(name()='id')] | node()"/>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

когда при этом обратное преобразование применяется к результату приведенного выше преобразования сглаживания, создается исходный XML-документ :

<root>
   <A>
      <B att="val">
         <C>
                someData
            </C>
      </B>
   </A>
   <A>
      <B>
             someOtherData
        </B>
      <B>
            moreData
        </B>
   </A>
</root>
3 голосов
/ 24 ноября 2010

Эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:variable name="vUppercase" select="'QWERTYUIOPASDFGHJKLZXCVBNM'"/>
    <xsl:variable name="vLowercase" select="'qwertyuiopasdfghjklzxcvbnm'"/>
    <xsl:template match="*">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:if test="parent::*/parent::*">
                <xsl:attribute name="id">
                    <xsl:value-of select="translate(name(),
                                                    $vUppercase,
                                                    $vLowercase)"/>
                    <xsl:number level="any"/>
                </xsl:attribute>
            </xsl:if>
            <xsl:apply-templates mode="ref"/>
        </xsl:copy>
        <xsl:apply-templates/>
    </xsl:template>
    <xsl:template match="*" mode="ref">
        <xsl:copy>
            <xsl:attribute name="ref">
                <xsl:value-of select="translate(name(),
                                                $vUppercase,
                                                $vLowercase)"/>
                <xsl:number level="any"/>
            </xsl:attribute>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="text()"/>
    <xsl:template match="/*">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Вывод:

<root>
    <A>
        <B ref="b1" />
    </A>
    <B att="val" id="b1">
        <C ref="c1" />
    </B>
    <C id="c1">
                    someData
    </C>
    <A>
        <B ref="b2" />
        <B ref="b3" />
    </A>
    <B id="b2">
                 someOtherData
    </B>
    <B id="b3">
                moreData
    </B>
</root>

Обратная таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kElementById" match="*[@id]" use="@id"/>
    <xsl:key name="kElementByRef" match="*[@ref]" use="@ref"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[key('kElementByRef',@id)]|
                         *[key('kElementByRef',@id)]/@id"/>
    <xsl:template match="*[@ref]">
        <xsl:for-each select="key('kElementById',@ref)">
            <xsl:call-template name="identity"/>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

Вывод:

<root>
    <A>
        <B att="val">
            <C>
                someData
            </C>
        </B>
    </A>
    <A>
        <B>
             someOtherData
        </B>
        <B>
            moreData
        </B>
    </A>
</root>
2 голосов
/ 24 ноября 2010

Возможно, вам лучше использовать таблицы стилей @ Alejandro или @ Dimitre, но я хотел опубликовать свою, так как я закончил рабочую версию:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:output method="xml" indent="yes"/>
   <xsl:template match="/*">
      <xsl:copy>
         <!-- copy any non-elements -->
         <xsl:copy-of select="@* | node()[not(self::*)]"/>
         <!-- transform descendant elements -->
         <xsl:apply-templates select=".//*" mode="define" />
      </xsl:copy>
   </xsl:template>

   <xsl:template match="*" mode="define">
      <xsl:copy>
         <xsl:attribute name="id"><xsl:value-of select="generate-id()"/></xsl:attribute>
         <xsl:copy-of select="@*" />
         <xsl:apply-templates select="node()" />
      </xsl:copy>
   </xsl:template>

   <xsl:template match="*">
      <xsl:copy>
         <xsl:attribute name="ref"><xsl:value-of select="generate-id()"/></xsl:attribute>
      </xsl:copy>
   </xsl:template>

   <!-- Identity transform -->
   <xsl:template match="@* | node()" mode="ref">
      <xsl:copy>
         <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
   </xsl:template>

</xsl:stylesheet>

Примечание: я не пытался сохранить

  • определенные шаблоны идентификаторов, которые вы использовали.Я предполагаю, что вам все равно, что это за идентификаторы, если они уникальны и стабильны.Если это предположение неверно, два предыдущих ответа показывают, как генерировать идентификаторы в соответствии с вашим шаблоном.
  • порядок, в котором вы создали определения элементов, хотя порядок исходного документа должен быть восстановлен из моего вывода.
  • тот факт, что ваши элементы верхнего уровня не имеют атрибутов id.Это было бы достаточно легко добавить функцию, как и другие ответы.Но, надеюсь, в этом нет необходимости: элементы верхнего уровня могут быть идентифицированы как таковые, потому что на них нет ссылок.

Когда я запускаю мою таблицу стилей на вашем примере ввода, я получаю этот вывод (интервал уродливно я не собираюсь это исправлять, так как у вас есть другие хорошие ответы):

<?xml version="1.0" encoding="utf-8"?>
<root>



   <A id="d0e3">

      <B ref="d0e5"/>

   </A>
   <B id="d0e5" att="val">

      <C ref="d0e7"/>

   </B>
   <C id="d0e7">
            someData
         </C>
   <A id="d0e12">

      <B ref="d0e14"/>

      <B ref="d0e17"/>

   </A>
   <B id="d0e14">
         someOtherData
      </B>
   <B id="d0e17">
         moreData
      </B>
</root>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...