XSLT 2.
Привет, у меня есть xml, который имеет 3 узла, названных с точки зрения «детей»: «Дети», «Отцы» и «Отцы-матери». Начиная с узла «Отцы», мне нужно найти дочерний узел MothersFather на основе идентификаторов в дочерних узлах (дочерний узел является промежуточной ссылкой, соединяющей два других).
Итак, для каждого Отца получите отдельных Матери Отцов своих детей - это не люди, у отца могут быть сотни детей, но только двадцать или около того связанных Матери Отцов :)
Упрощенная версия XML (в реальной жизни имеется около 80 узлов-отцов, 3000 узлов-потомков и 400 узлов-отцов-матерей):
<t>
<Children>
<Child>
<ID>1</ID>
<FathersID>100</FathersID>
<MothersFatherID>200</MothersFatherID>
</Child>
<Child>
<ID>2</ID>
<FathersID>100</FathersID>
<MothersFatherID>201</MothersFatherID>
</Child>
<Child>
<ID>3</ID>
<FathersID>100</FathersID>
<MothersFatherID>202</MothersFatherID>
</Child>
<Child>
<ID>4</ID>
<FathersID>100</FathersID>
<MothersFatherID>201</MothersFatherID>
</Child>
<Child>
<ID>5</ID>
<FathersID>101</FathersID>
<MothersFatherID>201</MothersFatherID>
</Child>
</Children>
<Fathers>
<Father>
<ID>100</ID>
</Father>
<Father>
<ID>101</ID>
</Father>
</Fathers>
<MothersFathers>
<MothersFather>
<ID>200</ID>
</MothersFather>
<MothersFather>
<ID>201</ID>
</MothersFather>
<MothersFather>
<ID>202</ID>
</MothersFather>
</MothersFathers>
</t>
Мой xslt выглядит так:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kFathersChildren" match="Child" use="FathersID"/>
<xsl:template match="/">
<xsl:apply-templates select="//Fathers"></xsl:apply-templates>
</xsl:template>
<xsl:template match="Fathers">
<xsl:apply-templates select="Father"></xsl:apply-templates>
</xsl:template>
<xsl:template match="Father">
<xsl:text> FATHER: ID=</xsl:text><xsl:value-of select="ID"/>
<!-- Now show all this fathers childrens maternal grandfathers based on the ID in the Child node -->
<!--TRY 1: this works, as in gets the right nodes, but doesn't do distinct values....-->
<xsl:for-each select="key('kFathersChildren', ID)"> <!-- get the fathers children -->
<xsl:text> found child: current MFid=</xsl:text><xsl:value-of select="current()/MothersFatherID"/>
<xsl:text> ID=</xsl:text><xsl:value-of select="ID"/>
<xsl:apply-templates select="//MothersFathers/MothersFather[ID=current()/MothersFatherID]"></xsl:apply-templates>
</xsl:for-each>
<!-- *** THIS IS WHERE I GET LOST??? - Do the same thing but only get distinct MothersFatherID's... -->
<!--TRY 2: note- won't compile in current state... -->
<xsl:for-each select="distinct-values(key('kFathersChildren', ID)[MothersFatherID])">
<xsl:text> Distinct MothersFatherID ???? - don't know what to select </xsl:text><xsl:value-of select="."/>
<xsl:apply-templates select="//MothersFathers/MothersFather[ID=??????????"></xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="//MothersFathers/MothersFather">
<xsl:text> IN MothersFather template... ID=</xsl:text><xsl:value-of select="ID"/>
</xsl:template>
</xsl:stylesheet>
В Try 1 я могу получить все узлы и MothersFatherID. Результат Try1:
FATHER: ID=100
found child: current MFid=200 ID=1
IN MothersFather template... ID=200
found child: current MFid=201 ID=2
IN MothersFather template... ID=201
found child: current MFid=202 ID=3
IN MothersFather template... ID=202
found child: current MFid=201 ID=4
IN MothersFather template... ID=201
FATHER: ID=101
found child: current MFid=201 ID=5
IN MothersFather template... ID=201
В Try2, где я выбираю «отдельное значение», я хотел бы получить следующий результат:
FATHER: ID=100
IN MothersFather template... ID=201
IN MothersFather template... ID=200
IN MothersFather template... ID=202
FATHER: ID=101
IN MothersFather template... ID=201
(это не настоящий вывод - просто отлаживайте материал, показывающий, что я могу ссылаться на правильные узлы).
НО я не могу понять, что я должен использовать для ссылки на уникальный MothersFatherID, который нужно передать вызову «apply-templates».
Независимо от того, что я пробовал, я получаю варианты ошибок, например: Required item type of first operand of '/' is node(); supplied value has item type xs:anyAtomicType
или Axis step child::element('':MothersFatherID) cannot be used here: the context item is an atomic value
. Я думаю, они имеют в виду, что я пытаюсь выбрать узлы, в которых используется строковое значение, или наоборот .... может быть, я неправильно использую функцию отличного значения ()?
Кто-нибудь может пролить свет на то, как это сделать, пожалуйста? (Я продолжаю надеяться, что у этого xslt будет какой-то дзен-момент просветления, когда я не буду зацикливаться на подобных вещах).
Вдобавок, как только я это сделаю, я захочу, чтобы MothersFather в отсортированном порядке для каждого отца - в реальном xml есть `` Имя '', связанное с каждым `` идентификатором '' - надеюсь, выражение для каждого `` сортировки '' будет аналогичная ссылка на то, что устраняет вышеуказанную проблему?
Спасибо за ваше время. Брайс.
РЕДАКТИРОВАТЬ:
Ух ты!! Спасибо за ответ Димитр. Я просмотрел это и надеялся, что вы сможете немного разбить его для меня, поскольку я не полностью его грок? Ответ был такой:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kMFByFId" match="MothersFatherID"
use="../FathersID"/>
<xsl:key name="kMFById" match="MothersFather" use="ID"/>
<xsl:key name="ChildByFIdAndMFId" match="Child"
use="concat(FathersID, '+', MothersFatherID)"/>
<xsl:template match="Children|MothersFathers|text()"/>
<xsl:template match="Father">
Father ID=<xsl:value-of select="ID"/>
<xsl:apply-templates select=
"key('kMFById',
key('kMFByFId', ID)
[generate-id(..)
=
generate-id(key('ChildByFIdAndMFId',
concat(../FathersID,'+',.)
)[1]
)
]
)">
<xsl:sort select="ID" data-type="number"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="MothersFather">
MothersFather ID=<xsl:value-of select="ID"/>
</xsl:template>
</xsl:stylesheet>
Я использую задействованные ключи.
Строка <xsl:template match="Children|MothersFathers|text()"/>
- как эта линия делает свое дело? Если я пропущу его через отладчик, он просто перескочит прямо за эту строку. Если я прокомментирую это, будет много лишнего вывода, источник которого я не вижу.
И строка apply-templates, которая дает узел MothersFather <xsl:apply-templates select= "key('kMFById', key('kMFByFId', ID)[generate-id(..) =
- я пытался разобрать это на бумаге, чтобы увидеть магию, но не совсем понял. Это что-то вроде
generate-id(key('ChildByFIdAndMFId', concat(../FathersID,'+',.))[1] ) ] )">key('kMFById', key('kMFByFId', ID)
означает получение соответствующих узлов MothersFather по текущему идентификатору отца, где [generate-id(..)
сгенерированный идентификатор '(точка точка)' - что-то связанное с родительским узлом? который из? равен сгенерированному идентификатору на основе ключа ChildByFIdAndMFId [1]
- получает ли этот 1 только первое вхождение совпадающего сгенерированного идентификатора, тем самым давая мое отдельное значение?
(Этот ответ Димитра также очень похож на ответ Дж. Л. Риши. Кажется, его вид работает, я что-то упускаю, Димитр?)
С уважением, Брайс.