如何在 XSLT 中重新排序标记化列表并一次从中读取两个值?
How to re-order a tokenized list in XSLT and read two values from it at a time?
我有一些代码(来自 GeoNetwork)需要将地理标记语言(在 XML 中)转换为 GeoJSON。我目前正在尝试添加功能以读取由 posList 形成的多边形,但我很难 conceptualizing/drafting 弄清楚我需要做什么。
'input' 基本上是由一堆坐标组成的字符串。所以它可能看起来像这样
<gml:LinearRing gml:id="p21" srsName="http://www.opengis.net/def/crs/EPSG/0/4326">
<gml:posList srsDimension="2">45.67 88.56 55.56 88.56 55.56 89.44 45.67 89.44</gml:posList>
</gml:LinearRing >
(借自维基百科的样本)。
我可以使用
之类的东西在 XSLT 中将其分块
<xsl:variable name="temp" as="xs:string*" select="tokenize(gml:LinearRing/gml:posList))" '\s'/>
哪个应该给我 Temp =
('45.67', '88.56', '55.56', '88.56', '55.56', '89.44', '45.67', '89.44')
问题 1:GeoJSON 想要 WGS 84 (EPSG 4326) 和(长,纬度)顺序中的所有内容 - 但严格遵守 WGS 84 规则(我希望 gml 遵循)意味着坐标在(lat, long) order - 所以列表需要重新排序。 (我想 - 这仍然让我很困惑)
问题2:GeoJSON想要坐标对,但我只有一个坐标列表。
我目前的想法是做这样的事情:
<geom>
<xsl:text>{"type": "Polygon",</xsl:text>
<xsl:text>"coordinates": [
[</xsl:text>
<xsl:variable name="temp" as="xs:string*" select="tokenize(gml:LinearRing/gml:posList))" '\s'/>
<xsl:for-each select="$temp">
<xsl:if test="position() mod 2 = 0">
<xsl:value-of select="concat('[', $saved, ', ', ., ']')" separator=","/>
</xsl:if>
<xsl:variable name="saved" value="."/>
</xsl:for-each>
<xsl:text>]
]
}</xsl:text>
</geom>
但我不确定XSL是否会让我这样连续写一个变量,以及是否有better/more-efficient解决问题的方法。 (我在 MATLAB 方面有很多经验,我会使用 for 循环快速解决这个问题,如果效率不高的话)
理想情况下我会得到类似于
的输出
<geom>
{"type": "Polygon",
"coordinates": [
[
[88.56, 45.67],
[88.56, 55.56],
[89.44, 55.56],
[89.44, 45.67]
]
]
}
</geom>
(我认为,要确定多边形是右手还是左手,还有一整套其他难题)
您可以使用以下 XSLT-2.0 样式表来获得您想要的结果。它使用 xsl:analyze-string
函数来分隔二元组中的值。该模板包括错误处理,并使用 exclude-result-prefixes="gml"
从输出中删除了目标命名空间 gml
。您可能需要调整模板的 XML 路径和 xsl:analyze-string
表达式。但我想你可以处理这个。
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:gml="http://www.opengis.net/def/crs/EPSG/0/4326" exclude-result-prefixes="gml">
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="/">
<geom><xsl:text>
{"type": "Polygon",
"coordinates": [
[
</xsl:text>
<xsl:analyze-string select="gml:LinearRing/gml:posList"
regex="\s*(\d\d)\.(\d\d)\s+(\d\d)\.(\d\d)\s*">
<xsl:matching-substring>
<xsl:value-of select="concat(' [',regex-group(3),'.', regex-group(4),', ',regex-group(1),'.', regex-group(2),']
')"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:message terminate="yes">=============================
=== ERROR: Invalid input! ===
=============================</xsl:message>
</xsl:non-matching-substring>
</xsl:analyze-string>
<xsl:text> ]
]
}
</xsl:text>
</geom>
</xsl:template>
</xsl:stylesheet>
其输出为:
<geom>
{"type": "Polygon",
"coordinates": [
[
[88.56, 45.67]
[88.56, 55.56]
[89.44, 55.56]
[89.44, 45.67]
]
]
}
</geom>%
此样式表包含任何输入(未使用)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:my="dummy"
exclude-result-prefixes="my">
<xsl:template match="/">
<xsl:sequence select="
my:reverseByTuple(
('45.67', '88.56', '55.56', '88.56', '55.56', '89.44', '45.67', '89.44')
)"/>
</xsl:template>
<xsl:function name="my:reverseByTuple">
<xsl:param name="items"/>
<xsl:sequence
select="if (empty($items))
then ()
else ($items[2], $items[1], my:reverseByTuple($items[position()>2]))"
/>
</xsl:function>
</xsl:stylesheet>
输出
88.56 45.67 88.56 55.56 89.44 55.56 89.44 45.67
我真的不明白你为什么要序列化 JSON 而不是像 XSLT 3.0 中的函数那样使用文档完善的库...但是为了好玩,这个样式表
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:my="dummy"
exclude-result-prefixes="my">
<xsl:template match="/">
<xsl:value-of
select="
my:encloseWithBracket(
my:reverseByTupleEncloseWithBracket(
('45.67', '88.56', '55.56', '88.56', '55.56', '89.44', '45.67', '89.44')
)
)"/>
</xsl:template>
<xsl:function name="my:reverseByTupleEncloseWithBracket">
<xsl:param name="items"/>
<xsl:sequence
select="if (empty($items))
then ()
else (my:encloseWithBracket(($items[2],$items[1])),
my:reverseByTupleEncloseWithBracket($items[position()>2]) )"
/>
</xsl:function>
<xsl:function name="my:encloseWithBracket">
<xsl:param name="items"/>
<xsl:value-of select="concat('[',string-join($items,','),']')"/>
</xsl:function>
</xsl:stylesheet>
输出
[[88.56,45.67],[88.56,55.56],[89.44,55.56],[89.44,45.67]]
具有 XPath 3.1 支持的 XSLT 3 可以将 JSON 表示为 maps/arrays 并将它们序列化为 JSON 因此您可以根据坐标序列计算 XPath 映射:
serialize(
map {
'type' : 'polygon',
'coordinates' : array {
let $seq := tokenize(gml:LinearRing/gml:posList, '\s+')
return $seq[position() mod 2 = 0]![., let $p := position() return $seq[($p - 1) * 2 + 1]]
}
},
map { 'method' : 'json', 'indent' : true() }
)
https://xsltfiddle.liberty-development.net/gWvjQfu/1
要在数组中获取 JSON 个数字,请使用 let $seq := tokenize(., '\s+')!number()
而不是 let $seq := tokenize(gml:LinearRing/gml:posList, '\s+')
。
如果您可以访问支持高阶函数的 XSLT 3 处理器,例如 Saxon PE 或 EE 或 Altova,您可以将其减少到
serialize(
map {
'type': 'polygon',
'coordinates': array {
let $seq := tokenize(gml:LinearRing/gml:posList, '\s+'),
$odd := $seq[position() mod 2 = 1],
$even := $seq[position() mod 2 = 0]
return
for-each-pair($odd, $even, function ($c1, $c2) {
[$c2, $c1]
})
}
},
map {
'method': 'json',
'indent': true()
}
)
我有一些代码(来自 GeoNetwork)需要将地理标记语言(在 XML 中)转换为 GeoJSON。我目前正在尝试添加功能以读取由 posList 形成的多边形,但我很难 conceptualizing/drafting 弄清楚我需要做什么。
'input' 基本上是由一堆坐标组成的字符串。所以它可能看起来像这样
<gml:LinearRing gml:id="p21" srsName="http://www.opengis.net/def/crs/EPSG/0/4326">
<gml:posList srsDimension="2">45.67 88.56 55.56 88.56 55.56 89.44 45.67 89.44</gml:posList>
</gml:LinearRing >
(借自维基百科的样本)。 我可以使用
之类的东西在 XSLT 中将其分块<xsl:variable name="temp" as="xs:string*" select="tokenize(gml:LinearRing/gml:posList))" '\s'/>
哪个应该给我 Temp =
('45.67', '88.56', '55.56', '88.56', '55.56', '89.44', '45.67', '89.44')
问题 1:GeoJSON 想要 WGS 84 (EPSG 4326) 和(长,纬度)顺序中的所有内容 - 但严格遵守 WGS 84 规则(我希望 gml 遵循)意味着坐标在(lat, long) order - 所以列表需要重新排序。 (我想 - 这仍然让我很困惑)
问题2:GeoJSON想要坐标对,但我只有一个坐标列表。
我目前的想法是做这样的事情:
<geom>
<xsl:text>{"type": "Polygon",</xsl:text>
<xsl:text>"coordinates": [
[</xsl:text>
<xsl:variable name="temp" as="xs:string*" select="tokenize(gml:LinearRing/gml:posList))" '\s'/>
<xsl:for-each select="$temp">
<xsl:if test="position() mod 2 = 0">
<xsl:value-of select="concat('[', $saved, ', ', ., ']')" separator=","/>
</xsl:if>
<xsl:variable name="saved" value="."/>
</xsl:for-each>
<xsl:text>]
]
}</xsl:text>
</geom>
但我不确定XSL是否会让我这样连续写一个变量,以及是否有better/more-efficient解决问题的方法。 (我在 MATLAB 方面有很多经验,我会使用 for 循环快速解决这个问题,如果效率不高的话)
理想情况下我会得到类似于
的输出<geom>
{"type": "Polygon",
"coordinates": [
[
[88.56, 45.67],
[88.56, 55.56],
[89.44, 55.56],
[89.44, 45.67]
]
]
}
</geom>
(我认为,要确定多边形是右手还是左手,还有一整套其他难题)
您可以使用以下 XSLT-2.0 样式表来获得您想要的结果。它使用 xsl:analyze-string
函数来分隔二元组中的值。该模板包括错误处理,并使用 exclude-result-prefixes="gml"
从输出中删除了目标命名空间 gml
。您可能需要调整模板的 XML 路径和 xsl:analyze-string
表达式。但我想你可以处理这个。
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:gml="http://www.opengis.net/def/crs/EPSG/0/4326" exclude-result-prefixes="gml">
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="/">
<geom><xsl:text>
{"type": "Polygon",
"coordinates": [
[
</xsl:text>
<xsl:analyze-string select="gml:LinearRing/gml:posList"
regex="\s*(\d\d)\.(\d\d)\s+(\d\d)\.(\d\d)\s*">
<xsl:matching-substring>
<xsl:value-of select="concat(' [',regex-group(3),'.', regex-group(4),', ',regex-group(1),'.', regex-group(2),']
')"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:message terminate="yes">=============================
=== ERROR: Invalid input! ===
=============================</xsl:message>
</xsl:non-matching-substring>
</xsl:analyze-string>
<xsl:text> ]
]
}
</xsl:text>
</geom>
</xsl:template>
</xsl:stylesheet>
其输出为:
<geom>
{"type": "Polygon",
"coordinates": [
[
[88.56, 45.67]
[88.56, 55.56]
[89.44, 55.56]
[89.44, 45.67]
]
]
}
</geom>%
此样式表包含任何输入(未使用)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:my="dummy"
exclude-result-prefixes="my">
<xsl:template match="/">
<xsl:sequence select="
my:reverseByTuple(
('45.67', '88.56', '55.56', '88.56', '55.56', '89.44', '45.67', '89.44')
)"/>
</xsl:template>
<xsl:function name="my:reverseByTuple">
<xsl:param name="items"/>
<xsl:sequence
select="if (empty($items))
then ()
else ($items[2], $items[1], my:reverseByTuple($items[position()>2]))"
/>
</xsl:function>
</xsl:stylesheet>
输出
88.56 45.67 88.56 55.56 89.44 55.56 89.44 45.67
我真的不明白你为什么要序列化 JSON 而不是像 XSLT 3.0 中的函数那样使用文档完善的库...但是为了好玩,这个样式表
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:my="dummy"
exclude-result-prefixes="my">
<xsl:template match="/">
<xsl:value-of
select="
my:encloseWithBracket(
my:reverseByTupleEncloseWithBracket(
('45.67', '88.56', '55.56', '88.56', '55.56', '89.44', '45.67', '89.44')
)
)"/>
</xsl:template>
<xsl:function name="my:reverseByTupleEncloseWithBracket">
<xsl:param name="items"/>
<xsl:sequence
select="if (empty($items))
then ()
else (my:encloseWithBracket(($items[2],$items[1])),
my:reverseByTupleEncloseWithBracket($items[position()>2]) )"
/>
</xsl:function>
<xsl:function name="my:encloseWithBracket">
<xsl:param name="items"/>
<xsl:value-of select="concat('[',string-join($items,','),']')"/>
</xsl:function>
</xsl:stylesheet>
输出
[[88.56,45.67],[88.56,55.56],[89.44,55.56],[89.44,45.67]]
具有 XPath 3.1 支持的 XSLT 3 可以将 JSON 表示为 maps/arrays 并将它们序列化为 JSON 因此您可以根据坐标序列计算 XPath 映射:
serialize(
map {
'type' : 'polygon',
'coordinates' : array {
let $seq := tokenize(gml:LinearRing/gml:posList, '\s+')
return $seq[position() mod 2 = 0]![., let $p := position() return $seq[($p - 1) * 2 + 1]]
}
},
map { 'method' : 'json', 'indent' : true() }
)
https://xsltfiddle.liberty-development.net/gWvjQfu/1
要在数组中获取 JSON 个数字,请使用 let $seq := tokenize(., '\s+')!number()
而不是 let $seq := tokenize(gml:LinearRing/gml:posList, '\s+')
。
如果您可以访问支持高阶函数的 XSLT 3 处理器,例如 Saxon PE 或 EE 或 Altova,您可以将其减少到
serialize(
map {
'type': 'polygon',
'coordinates': array {
let $seq := tokenize(gml:LinearRing/gml:posList, '\s+'),
$odd := $seq[position() mod 2 = 1],
$even := $seq[position() mod 2 = 0]
return
for-each-pair($odd, $even, function ($c1, $c2) {
[$c2, $c1]
})
}
},
map {
'method': 'json',
'indent': true()
}
)