Вложенные условные операторы if else в xpath

У меня есть этот XML:

<property id="1011">
    <leasehold>No</leasehold>
    <freehold>Yes</freehold>
    <propertyTypes>
        <propertyType>RESIDENTIAL</propertyType>
    </propertyTypes>
</property>

и я хочу создать инструкцию xpath, аналогичную следующему вложенному блоку псевдокода if-else.

if( propertyTypes/propertyType == 'RESIDENTIAL') {
    if( leasehold == 'Yes' ){
        return 'Rent'
    } else
        return 'Buy'
    }
} else {
    if( leasehold == 'Yes' ){
        return 'Leasehold'
    } else
        return 'Freehold'
    }
}

Я кое-что видел о методе Беккера, но не мог его понять. На самом деле XPath не моя сильная сторона.


person Kirk WSI    schedule 19.10.2012    source источник


Ответы (4)


И. В XPath 2.0 это просто преобразуется в:

   if(/*/propertyTypes/propertyType = 'RESIDENTIAL')
    then
     (if(/*/leasehold='Yes')
       then 'Rent'
       else 'Buy'
     )
     else
       if(/*/leasehold='Yes')
         then 'Leasehold'
         else 'Freehold'

Проверка на основе XSLT 2.0:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:sequence select=
   "if(/*/propertyTypes/propertyType = 'RESIDENTIAL')
   then
     (if(/*/leasehold='Yes')
       then 'Rent'
       else 'Buy'
     )
     else
       if(/*/leasehold='Yes')
         then 'Leasehold'
         else 'Freehold'
   "/>
 </xsl:template>
</xsl:stylesheet>

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

<property id="1011">
    <leasehold>No</leasehold>
    <freehold>Yes</freehold>
    <propertyTypes>
        <propertyType>RESIDENTIAL</propertyType>
    </propertyTypes>
</property>

выражение XPath оценивается, и результат этой оценки копируется в выходные данные:

Buy

II. Решение XPath 1.0

В XPath 1.0 нет оператора if.

Условный оператор по-прежнему может быть реализован с помощью одного выражения XPath 1.0, но это более сложно, и выражение может быть не слишком читабельным и понятным.

Вот общий способ (впервые предложенный Джени Теннисон) для создания $stringA, когда условие $cond является true(), и в противном случае для создания $stringB:

concat(substring($stringA, 1 div $cond), substring($stringB, 1 div not($cond)))

Одним из основных преимуществ этой формулы является то, что она работает со строками любой длины и не требует указания длины.

Пояснение:

Здесь мы используем тот факт, что по определению:

number(true()) = 1

и

number(false()) = 0

и что

1 div 0 = Infinity

Итак, если $cond равно false, первый аргумент concat() выше:

 substring($stringA, Infinity)

и это пустая строка, потому что $stringA имеет конечную длину.

С другой стороны, если $cond равно true(), то первый аргумент concat() выше:

sibstring($stringA, 1) 

это просто $stringA.

Таким образом, в зависимости от значения $cond только один из двух аргументов concat() выше является непустой строкой (соответственно $stringA или $stringB).

Применяя эту общую формулу к конкретному вопросу, мы можем преобразовать первую половину большого условного выражения в:

concat(
           substring('rent',
                      1 div boolean(/*[leasehold='Yes'
                                     and
                                       propertyTypes/propertyType = 'RESIDENTIAL'
                                      ]
                                  )
                      ),
           substring('buy',
                      1 div not(/*[leasehold='Yes'
                                     and
                                       propertyTypes/propertyType = 'RESIDENTIAL'
                                      ]
                                  )
                      )
               )

Это должно дать вам представление о том, как преобразовать все условное выражение в одно выражение XPath 1.0.

Проверка на основе XSLT 1.0:

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

 <xsl:template match="/">
  <xsl:copy-of select=
   "concat(
           substring('rent',
                      1 div boolean(/*[leasehold='Yes'
                                     and
                                       propertyTypes/propertyType = 'RESIDENTIAL'
                                      ]
                                  )
                      ),
           substring('buy',
                      1 div not(/*[leasehold='Yes'
                                     and
                                       propertyTypes/propertyType = 'RESIDENTIAL'
                                      ]
                                  )
                      )
               )
   "/>
 </xsl:template>
</xsl:stylesheet>

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

buy

Обратите внимание:

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

person Dimitre Novatchev    schedule 19.10.2012
comment
Хорошее объяснение. Есть ли способ добавить длину узла в виде $cond? - person Daniel; 02.04.2019
comment
@dan, как объяснено, в этом нет необходимости. Таким образом, даже если длина строки неизвестна или изменяется со временем, это выражение по-прежнему выражает желаемую условную оценку. Да, можно использовать длины строк, но выражение, включающее их, очень хрупкое и зависит от длины. - person Dimitre Novatchev; 02.04.2019

Метод Беккера для ваших данных следующий:

concat(substring('Rent',      1 div boolean(propertyTypes/propertyType ="RESIDENTIAL" and leasehold="Yes")),
       substring('Buy',       1 div boolean(propertyTypes/propertyType ="RESIDENTIAL" and leasehold="No")),
       substring('Leasehold', 1 div boolean(propertyTypes/propertyType!="RESIDENTIAL" and leasehold="Yes")),
       substring('Freehold',  1 div boolean(propertyTypes/propertyType!="RESIDENTIAL" and leasehold="No")))
person choroba    schedule 19.10.2012
comment
choroba, вас может заинтересовать более общее выражение XPath 1.0, которое не требует указания длины (и изменения всякий раз, когда мы хотим использовать в качестве результатов разные строки). - person Dimitre Novatchev; 19.10.2012

Сегодня провел весь день, но у меня работает это для Xpath 1.0:

concat(
substring(properties/property[@name="Headline"], 1, string-length(properties/property[@name="Headline"]) * 1), 
substring(properties/property[@name="Name"], 1, not(number(string-length(properties/property[@name="Headline"]))) * string-length(properties/property[@name="Name"]))
)
person Pavel Philippenko    schedule 11.11.2015

Попробуй это

if (condition) 
then 
  if (condition) stmnt 
  else stmnt 
else 
  if (condition) stmnt 
  else stmnt 
person Mirko Adari    schedule 19.10.2012