Решение для первой проблемы :
Это выражение XPath:
/*/div/a
|
/*/div[not(a)]
При сравнении со следующим документом XML:
<t>
<div>
<a>a1</a>
<b>b1</b>
</div>
<div>
<b>b2</b>
</div>
<div>
<a>a3</a>
<b>b3</b>
<c>c3</c>
</div>
</t>
выбирает следующие три узла (a
, div
, a
):
<a>a1</a>
<div>
<b>b2</b>
</div>
<a>a3</a>
В вашем массиве Java любой выбранный элемент, не являющийся a
, должен рассматриваться как (или заменяться)null
.
Вот одно решение второй проблемы :
Используйте эти выражения XPath для выбора элементов a
из каждогогруппа :
для первой группы:
/*/h1[1]
/following-sibling::a
[not(/*/h1[2])
or
count(.|/*/h1[2]/preceding-sibling::a)
=
count(/*/h1[2]/preceding-sibling::a)
]
для второй группы :
/*/h1[2]
/following-sibling::a
[not(/*/h1[3])
or
count(.|/*/h1[3]/preceding-sibling::a)
=
count(/*/h1[3]/preceding-sibling::a)
]
и для3-я группа :
/*/h1[3]
/following-sibling::a
[not(/*/h1[4])
or
count(.|/*/h1[4]/preceding-sibling::a)
=
count(/*/h1[4]/preceding-sibling::a)
]
В случае, если:
count(/*/h1
)
равно $cnt
,
, генерировать $cnt
такие выражения (для i = 1 to $cnt
) и оцените их все.Выбранные узлы по каждому из них либо содержат элемент a
, либо нет.Если $k
-ая группа (узлы, выбранные из вычисления $ k-го выражения) содержит a
, используйте ее строковое значение для генерации $k
-го элемента требуемого массива - в противном случае создайте null
для $k
-ого элемента искомого массива.
Вот проверка на основе XSLT вышеуказанных выражений XPath :
<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:variable name="vGroup1" select=
"/*/h1[1]
/following-sibling::a
[not(/*/h1[2])
or
count(.|/*/h1[2]/preceding-sibling::a)
=
count(/*/h1[2]/preceding-sibling::a)
]
"/>
<xsl:variable name="vGroup2" select=
"/*/h1[2]
/following-sibling::a
[not(/*/h1[3])
or
count(.|/*/h1[3]/preceding-sibling::a)
=
count(/*/h1[3]/preceding-sibling::a)
]
"/>
<xsl:variable name="vGroup3" select=
"/*/h1[3]
/following-sibling::a
[not(/*/h1[4])
or
count(.|/*/h1[4]/preceding-sibling::a)
=
count(/*/h1[4]/preceding-sibling::a)
]
"/>
Group1: "<xsl:copy-of select="$vGroup1"/>"
Group2: "<xsl:copy-of select="$vGroup2"/>"
Group3: "<xsl:copy-of select="$vGroup3"/>"
</xsl:template>
</xsl:stylesheet>
Когда это преобразование применяется к следующему документу XML (OP не предоставил полный и правильно сформированный документ XML !!!):
<t>
<h1>heading1</h1>
<a>a1</a>
<b>b1</b>
<h1>heading2</h1>
<b>b2</b>
<h1>heading3</h1>
<a>a3</a>
<b>b3</b>
<c>c3</c>
</t>
, три выражения XPathоцениваются и выбираются узлы по каждому из них: :
Group1: "<a>a1</a>"
Group2: ""
Group3: "<a>a3</a>"
Пояснение :
Используется известная формула Кейсиана для пересечения двух наборов узлов:
$ns1[count(. | $ns2) = count($ns2)]
Результат вычисления этого выражения содержит именно те узлы, которые принадлежат как к набору узлов $ns1
, так и к набору узлов $ns2
.
Осталось заменить $ns1
и $ns2
с выражениями, которые имеют отношение к проблеме.
Мы заменяем $ns1
на:
/*/h1[1]
/following-sibling::a
и заменяем $ns2
на:
/*/h1[2]
/preceding-sibling::a
В другихДругими словами, элементы a
, которые находятся между первым и вторым /*/h1
, являются пересечением элементов a
, которые следуют за братьями и сестрами /*/h1[1]
, и элементами a
, которые предшествуют братьям и сестрам /*/h1[2]
.
Это выражение проблематично только для элементов a
, следующих за последним из элементов /*/h1
.вот почему мы добавляем дополнительный предикат, который проверяет отсутствие следующего /*/h1
элемента и or
это со следующими логическими выражениями.
Наконец, в качестве руководящего примера дляРеализация Java здесь представляет собой полное преобразование XSLT, которое выполняет нечто подобное - создает сериализованный массив и может быть механически преобразовано в соответствующее решение Java :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
<xsl:output method="text"/>
<my:null>null</my:null>
<my:Q>"</my:Q>
<xsl:variable name="vNull" select="document('')/*/my:null"/>
<xsl:variable name="vQ" select="document('')/*/my:Q"/>
<xsl:template match="/">
<xsl:variable name="vGroup1" select=
"/*/h1[1]
/following-sibling::a
[not(/*/h1[2])
or
count(.|/*/h1[2]/preceding-sibling::a)
=
count(/*/h1[2]/preceding-sibling::a)
]
"/>
<xsl:variable name="vGroup2" select=
"/*/h1[2]
/following-sibling::a
[not(/*/h1[3])
or
count(.|/*/h1[3]/preceding-sibling::a)
=
count(/*/h1[3]/preceding-sibling::a)
]
"/>
<xsl:variable name="vGroup3" select=
"/*/h1[3]
/following-sibling::a
[not(/*/h1[4])
or
count(.|/*/h1[4]/preceding-sibling::a)
=
count(/*/h1[4]/preceding-sibling::a)
]
"/>
[<xsl:value-of select=
"concat($vQ[$vGroup1/self::a[1]],
$vGroup1/self::a[1],
$vQ[$vGroup1/self::a[1]],
$vNull[not($vGroup1/self::a[1])])"/>
<xsl:text>,</xsl:text>
<xsl:value-of select=
"concat($vQ[$vGroup2/self::a[1]],
$vGroup2/self::a[1],
$vQ[$vGroup2/self::a[1]],
$vNull[not($vGroup2/self::a[1])])"/>
<xsl:text>,</xsl:text>
<xsl:value-of select=
"concat($vQ[$vGroup3/self::a[1]],
$vGroup3/self::a[1],
$vQ[$vGroup3/self::a[1]],
$vNull[not($vGroup3/self::a[1])])"/>]
</xsl:template>
</xsl:stylesheet>
Когда это преобразованиеПрименительно к тому же XML-документу (выше), требуемый, правильный результат получается :
["a1",null,"a3"]
Update2 :
Теперь OP добавил, чтоон может использовать решение XSLT.Вот один из них:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my" exclude-result-prefixes="xsl">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kFollowing" match="a"
use="generate-id(preceding-sibling::h1[1])"/>
<my:null/>
<xsl:variable name="vNull" select="document('')/*/my:null"/>
<xsl:template match="/*">
<xsl:copy-of select=
"h1/following-sibling::a[1]
|
h1[not(key('kFollowing', generate-id()))]"/>
=============================================
<xsl:apply-templates select="h1"/>
</xsl:template>
<xsl:template match="h1">
<xsl:variable name="vAsInGroup" select=
"key('kFollowing', generate-id())"/>
<xsl:copy-of select="$vAsInGroup[1] | $vNull[not($vAsInGroup)]"/>
</xsl:template>
</xsl:stylesheet>
Это преобразование реализует два разных решения.Разница в том, какой элемент используется для представления «ноль».В первом случае это элемент h1
.Это не рекомендуется, потому что любой h1
уже имеет свое собственное значение, которое отличается от «представления нуля».Второе решение использует специальный элемент my:null
для представления нуля.
Когда это преобразование применяется к тому же XML-документу, что и выше :
<t>
<h1>heading1</h1>
<a>a1</a>
<b>b1</b>
<h1>heading2</h1>
<b>b2</b>
<h1>heading3</h1>
<a>a3</a>
<b>b3</b>
<c>c3</c>
</t>
каждое из двух выражений XPath (содержащее ссылки XSLT key()
) оцениваются и выводятся выбранные узлы (выше и ниже "========" соответственно):
<a>a1</a>
<h1>heading2</h1>
<a>a3</a>
=============================================
<a>a1</a>
<my:null xmlns:my="my:my" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"/>
<a>a3</a>
Замечание по производительности :
Поскольку используются ключи, это решение будет значительно более эффективным, когда выполняется более одного поиска - например, когда необходимо создать соответствующие массивы для a
, b
и c
.