Генерируйте XPath для каждого узла в XML, используя пользовательский атрибут (имя в этом случае) - PullRequest
4 голосов
/ 15 сентября 2011

У меня есть огромный XML-файл, который выглядит примерно так (но гораздо больше):

<?xml version="1.0" encoding="UTF-8"?>
<suite id="1" name="SuiteName">
  <displayNameKey>something</displayNameKey>
  <displayName>something</displayName>
  <application id="2" name="Manager">
    <displayNameKey>appName</displayNameKey>
    <displayName>appName</displayName>
    <category id="12" name="navigation">
      <displayNameKey>managerNavigation</displayNameKey>
      <displayName>managerNavigation</displayName>
      <description>mgr_navigation</description>
      <property id="13" name="httpPort" type="integer_property" width="40">
        <displayNameKey>managerHttpPort</displayNameKey>
        <displayName>managerHttpPort</displayName>
        <value>80</value>
      </property>
      <property id="14" name="httpsPort" type="integer_property" width="40">
        <displayNameKey>managerHttpsPort</displayNameKey>
        <displayName>managerHttpsPort</displayName>
        <value>443</value>
      </property>
      <property id="15" name="welcomePageURI" type="url_property" width="40" hidden="true">
        <displayNameKey>welcomePageURI</displayNameKey>
        <displayName>welcomePageURI</displayName>
        <value>jsp/index.jsp</value>
      </property>
      <property id="16" name="serverURL" type="url_property" width="40">
        <displayNameKey>serverURL</displayNameKey>
        <displayName>serverURL</displayName>
        <value>somevalue</value>
      </property>
    </category>
    <category id="17" name="datafiltering">
      <displayNameKey>managerDataFiltering</displayNameKey>
      <displayName>managerDataFiltering</displayName>
      <description>mgr_data_filtering</description>
      <property id="18" name="defaultTableName" type="string_property" width="40">
        <displayNameKey>defaultTableName</displayNameKey>
        <displayName>defaultTableName</displayName>
      </property>
      <property id="19" name="defaultAudienceName" type="string_property" width="40">
        <displayNameKey>defaultAudienceName</displayNameKey>
        <displayName>defaultAudienceName</displayName>
      </property>
    </category>
  </application>
</suite>

Что мне нужно сделать, это сгенерировать выражение XPath для каждого свойства, но без использования позиций или идентификаторовно имя атрибута.То есть для файла выше требуемый вывод похож на:

/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="httpPort"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="httpsPort"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="welcomePageURI"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="serverURL"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="datafiltering"]/property[@name="defaultTableName"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="datafiltering"]/property[@name="defaultAudienceName"]

Все найденные мной генераторы XPath генерируют только XPath, используя атрибут name или position, например / suite [0] / application [0] /категория [1] /...

Можете ли вы порекомендовать мне способ генерации XPath для всех свойств в моем файле?И еще одна вещь - структура является переменной - то есть может быть от 0 до N вложенных категорий, таких как

/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="cat1"]/category[@name="cat2"]/category[@name="cat3"]/property[@name="property1"]
/suite[@name="SuiteName"]/application[@name="Manager"]/property[@name="property2"]

Ответы [ 3 ]

1 голос
/ 15 сентября 2011

Вы можете сделать это в php следующим образом:

<?php

$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<suite id="1" name="SuiteName">
  <displayNameKey>something</displayNameKey>
  <displayName>something</displayName>
  <application id="2" name="Manager">
    <displayNameKey>appName</displayNameKey>
    <displayName>appName</displayName>
    <category id="12" name="navigation">
      <displayNameKey>managerNavigation</displayNameKey>
      <displayName>managerNavigation</displayName>
      <description>mgr_navigation</description>
      <property id="13" name="httpPort" type="integer_property" width="40">
        <displayNameKey>managerHttpPort</displayNameKey>
        <displayName>managerHttpPort</displayName>
        <value>80</value>
      </property>
      <property id="14" name="httpsPort" type="integer_property" width="40">
        <displayNameKey>managerHttpsPort</displayNameKey>
        <displayName>managerHttpsPort</displayName>
        <value>443</value>
      </property>
      <property id="15" name="welcomePageURI" type="url_property" width="40" hidden="true">
        <displayNameKey>welcomePageURI</displayNameKey>
        <displayName>welcomePageURI</displayName>
        <value>jsp/index.jsp</value>
      </property>
      <property id="16" name="serverURL" type="url_property" width="40">
        <displayNameKey>serverURL</displayNameKey>
        <displayName>serverURL</displayName>
        <value>somevalue</value>
      </property>
    </category>
    <category id="17" name="datafiltering">
      <displayNameKey>managerDataFiltering</displayNameKey>
      <displayName>managerDataFiltering</displayName>
      <description>mgr_data_filtering</description>
      <property id="18" name="defaultTableName" type="string_property" width="40">
        <displayNameKey>defaultTableName</displayNameKey>
        <displayName>defaultTableName</displayName>
      </property>
      <property id="19" name="defaultAudienceName" type="string_property" width="40">
        <displayNameKey>defaultAudienceName</displayNameKey>
        <displayName>defaultAudienceName</displayName>
      </property>
    </category>
  </application>
</suite>
XML;


function genXpath($xml, $att, $current = null)
{
    if($current == null) $current = '/*';
    $new = $current.'[@'.$att.']';

    $result = $xml->xpath($new);

    if($current[strlen($current) - 1] == '*')
    {
        $current = substr($current, 0, strlen($current) - 1);
    }

    if(count($result))
    {
        foreach($result as $node)
        {
            $prev = $current;
            $current .= $node->getName().'[@'.$att.'="'.$node->attributes()->$att.'"]/*';
            genXpath($xml, $att, $current);
            $current = $prev;
        }

    }
    else
    {
        $current = substr($current, 0, strlen($current) - 1);
        echo $current.'<br />';
    }

}

// how to use
$xml = new SimpleXMLElement($xml);
genXpath($xml, "name");

?>

Он выдаст что-то вроде этого:

/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="httpPort"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="httpsPort"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="welcomePageURI"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="serverURL"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="datafiltering"]/property[@name="defaultTableName"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="datafiltering"]/property[@name="defaultAudienceName"]

Надеюсь, это поможет.А также вы можете установить желаемое имя атрибута.

Сама функция и ее использование:

<?php

function genXpath($xml, $att, $current = null)
{
    if($current == null) $current = '/*';
    $new = $current.'[@'.$att.']';

    $result = $xml->xpath($new);

    if($current[strlen($current) - 1] == '*')
    {
        $current = substr($current, 0, strlen($current) - 1);
    }

    if(count($result))
    {
        foreach($result as $node)
        {
            $prev = $current;
            $current .= $node->getName().'[@'.$att.'="'.$node->attributes()->$att.'"]/*';
            genXpath($xml, $att, $current);
            $current = $prev;
        }

    }
    else
    {
        $current = substr($current, 0, strlen($current) - 1);
        echo $current.'<br />';
    }

}

// how to use
$xml = "your xml string"; // you can read it from a file   
$xml = new SimpleXMLElement($xml);
genXpath($xml, "name");

Алгоритм - вот что важно, вы можете легко перенести его в любойдругой язык программирования.Все, что нужно, - это поддержка xpath, и чтобы изменить способ получения информации из результата, полученного с помощью запроса xpath.

С уважением,слепая

1 голос
/ 16 сентября 2011

Это, вероятно, самый короткий и простой (без именованных шаблонов, вообще никаких явных условных инструкций, без xsl:for-each и без использования //) XSLT-преобразования, реализующего требуемую обработку :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="property">
  <xsl:apply-templates select="ancestor::*" mode="build"/>
  <xsl:value-of select=
  "concat('/property[@name=&quot;', @name, '&quot;]')"/>
  <xsl:text>&#xA;</xsl:text>
 </xsl:template>

 <xsl:template match="*" mode="build">
  <xsl:value-of select=
  "concat('/',name(),'[@name=&quot;', @name, '&quot;]')"/>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

когда это преобразование применяется к предоставленному документу XML :

<?xml version="1.0" encoding="UTF-8"?>
<suite id="1" name="SuiteName">
  <displayNameKey>something</displayNameKey>
  <displayName>something</displayName>
  <application id="2" name="Manager">
    <displayNameKey>appName</displayNameKey>
    <displayName>appName</displayName>
    <category id="12" name="navigation">
      <displayNameKey>managerNavigation</displayNameKey>
      <displayName>managerNavigation</displayName>
      <description>mgr_navigation</description>
      <property id="13" name="httpPort" type="integer_property" width="40">
        <displayNameKey>managerHttpPort</displayNameKey>
        <displayName>managerHttpPort</displayName>
        <value>80</value>
      </property>
      <property id="14" name="httpsPort" type="integer_property" width="40">
        <displayNameKey>managerHttpsPort</displayNameKey>
        <displayName>managerHttpsPort</displayName>
        <value>443</value>
      </property>
      <property id="15" name="welcomePageURI" type="url_property" width="40" hidden="true">
        <displayNameKey>welcomePageURI</displayNameKey>
        <displayName>welcomePageURI</displayName>
        <value>jsp/index.jsp</value>
      </property>
      <property id="16" name="serverURL" type="url_property" width="40">
        <displayNameKey>serverURL</displayNameKey>
        <displayName>serverURL</displayName>
        <value>somevalue</value>
      </property>
    </category>
    <category id="17" name="datafiltering">
      <displayNameKey>managerDataFiltering</displayNameKey>
      <displayName>managerDataFiltering</displayName>
      <description>mgr_data_filtering</description>
      <property id="18" name="defaultTableName" type="string_property" width="40">
        <displayNameKey>defaultTableName</displayNameKey>
        <displayName>defaultTableName</displayName>
      </property>
      <property id="19" name="defaultAudienceName" type="string_property" width="40">
        <displayNameKey>defaultAudienceName</displayNameKey>
        <displayName>defaultAudienceName</displayName>
      </property>
    </category>
  </application>
</suite>

желаемый, правильный результат получается :

/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="httpPort"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="httpsPort"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="welcomePageURI"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="serverURL"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="datafiltering"]/property[@name="defaultTableName"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="datafiltering"]/property[@name="defaultAudienceName"]
0 голосов
/ 15 сентября 2011

Я бы сделал что-то вроде этого:

<xsl:transform version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" />
    <xsl:template match="/">
    <xsl:for-each select="//property">
        <xsl:call-template name="add-parent-xpath"/>
        <xsl:text>
</xsl:text>
    </xsl:for-each>
    </xsl:template>

    <xsl:template name="add-parent-xpath">
        <xsl:if test="name(.) != 'suite'">
            <xsl:for-each select="..">
                <xsl:call-template name="add-parent-xpath" />
            </xsl:for-each>
        </xsl:if>
        <xsl:value-of select="concat('/', name(.), '[@name=&quot;', @name, '&quot;]')"/>
    </xsl:template>

</xsl:transform>

Он начинается с выбора каждого узла свойства, а затем рекурсивно поднимается обратно на узел комплекта. Когда рекурсия разворачивается, она испускает xpath для выбора этого узла, поэтому вы получаете xpath для набора, затем следующий уровень и т. Д. Вплоть до свойства.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...