Generate a XML from some defined XPATH
我正在尝试根据定义的XPATH从另一个XML生成XML。
XPATH:
1 2 3 4 5 6 7 8 9 10 11 | country/name, country/org_id, country/lang, country/currency, generate_date, schedule/category/id, schedule/category/name, schedule/category/classes/class/id, schedule/category/classes/class/duration, schedule/category/classes/class/price, schedule/category/classes/class/instruction_language |
Xpath排除了根节点的名称,它是一个列表。
XML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?xml version="1.0" encoding="utf-8" ?> <ou_schedule> <country> <name>Country Name</name> <org_id>Org ID</org_id> <lang>language</lang> <currency>Currency</currency> </country> <generate_date>Date</generate_date> <schedule> <category> <id>cat id</id> <name>Cat name</name> <classes> <class> <id>class id</id> <duration>class duration</duration> <price>price</price> <instruction_language>Test Data</instruction_language> </class> <class> <id>class id</id> <duration>class duration</duration> <price>price</price> <instruction_language>Test Data</instruction_language> </class> </classes> </category> </schedule> </ou_schedule> |
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <?xml version="1.0" encoding="utf-8"?> <ou_schedule> <country.name>country name</country.name> <country.org_id>org id</country.org_id> <country.lang>language</country.lang> <country.currency>currency</country.currency> <generate_date>date</generate_date> <schedule.category.name>Cat Name</schedule.category.name> <schedule.category.id>Cat ID</schedule.category.id> <schedule.category.classes.class.id>class id</schedule.category.classes.class.id> <schedule.category.classes.class.duration>class duration</schedule.category.classes.class.duration> <schedule.category.classes.class.price>price</schedule.category.classes.class.price> <schedule.category.classes.class.instruction_language>Test Data</schedule.category.classes.class.instruction_language> <country.name>country name</country.name> <country.org_id>org id</country.org_id> <country.lang>language</country.lang> <country.currency>currency</country.currency> <generate_date>date</generate_date> <schedule.category.name>Cat Name</schedule.category.name> <schedule.category.id>Cat ID</schedule.category.id> <schedule.category.classes.class.id>class id</schedule.category.classes.class.id> <schedule.category.classes.class.duration>class duration</schedule.category.classes.class.duration> <schedule.category.classes.class.price>price</schedule.category.classes.class.price> <schedule.category.classes.class.instruction_language>Test Data</schedule.category.classes.class.instruction_language> </ou_schedule> |
在这里,为了消除歧义,我用根节点除外的祖先命名节点名称,即与XPATH相同,但用
是否可以使用一些通用的
Is it possible to achieve this using some generic XSLT?
如果有两种解决方案:一种用于XSLT 1.0,另一种用于XSLT 2.0,则可以使用XSLT 2.0条件编译技术将它们(人为地)组合为一个,而在" XSLT 1.0解决方案的模板和声明。另一方面,XSLT 1.0解决方案将在前向兼容模式下运行,并且还将为其模板指定更高的优先级(高于XSLT 2.0解决方案模板的优先级),因此将不会选择XSLT 2.0解决方案的模板使用XSLT 1.0处理器运行转换时执行。
您可以将其视为一项有趣的练习,并按照Michael Kay的书中的示例" XSLT 2.0和XPath 2.0",第3章:"样式表结构","编写可移植的样式表"部分,小节:"有条件的编译""。示例(在我所拥有的版本中)在第128页。
这里是一个简短的XSLT 2.0解决方案(如果省略参数值,则为18行),纯(无扩展功能),不使用显式XSLT条件指令或任何
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <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:strip-space elements="*"/> <xsl:param name="pPaths" as="xs:string+" select= "'country/name', 'country/org_id', 'country/lang', 'country/currency', 'generate_date', 'schedule/category/id', 'schedule/category/name', 'schedule/category/classes/class/id', 'schedule/category/classes/class/duration', 'schedule/category/classes/class/price', 'schedule/category/classes/class/instruction_language'"/> <xsl:template match="/*"> <xsl:copy><xsl:apply-templates/></xsl:copy> </xsl:template> <xsl:template match= "*/*[string-join((ancestor::*[position() ne last()]| .)/name(), '/') = $pPaths]"> <xsl:element name="{string-join((ancestor::*[position() ne last()]|.)/name(), '.')}"> <xsl:value-of select="."/> </xsl:element> </xsl:template> <xsl:template match="text()"/> </xsl:stylesheet> |
解决方案2:
此处将资源(文件)的URI(文件路径)作为参数传递。此文件包含所有需要的XPath表达式-每个表达式都位于单独的一行中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <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:strip-space elements="*"/> <xsl:param name="pFilePath" select="'file:///C:/temp/expressions.txt'"/> <xsl:variable name="vExprs" select="tokenize(unparsed-text($pFilePath), '\ ?\ ')"/> <xsl:template match="/*"> <xsl:copy><xsl:apply-templates/></xsl:copy> </xsl:template> <xsl:template match= "*/*[string-join((ancestor::*[position() ne last()]| .)/name(), '/') = $vExprs]"> <xsl:element name= "{string-join((ancestor::*[position() ne last()]|.)/name(), '.')}"> <xsl:value-of select="."/> </xsl:element> </xsl:template> <xsl:template match="text()"/> </xsl:stylesheet> |
解决方案3:
如果已知输入XPath表达式选择的元素具有单个text-node子元素(并且最初提供的输入XPath表达式就是这种情况),则可以进一步优化和简化这两个先前的解决方案。 XML文档源):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <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:strip-space elements="*"/> <xsl:param name="pFilePath" select="'file:///C:/temp/expressions.txt'"/> <xsl:variable name="vExprs" select="tokenize(unparsed-text($pFilePath), '\ ?\ ')"/> <xsl:template match="/*"> <xsl:copy><xsl:apply-templates/></xsl:copy> </xsl:template> <xsl:template match= "text()[string-join(ancestor::*[position() ne last()]/name(), '/') = $vExprs]"> <xsl:element name="{string-join(ancestor::*[position() ne last()]/name(), '.')}"> <xsl:value-of select="."/> </xsl:element> </xsl:template> <xsl:template match="text()"/> </xsl:stylesheet> |
我的第一个想法是:有趣的是,在这里,我们将获得一个动态构建的XSL转换。但这似乎无法实现,因为xslt中的动态xpath解释了。
因此,需要第二个想法:您可以将XSL转换视为XPATH表达式的列表。从这个意义上讲,您只需要一个如下的XSLT文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" indent="yes"/> <!-- the following select-attributes are the set of XPATH expressions (relative to /ou_schedule/schedule/category/classes/class) --> <xsl:template name="XPathList"> <category_name> <xsl:apply-templates select="ancestor::category/name"/> </category_name> <category_id> <xsl:apply-templates select="ancestor::category/id"/> </category_id> <id> <xsl:apply-templates select="id"/> </id> <duration> <xsl:apply-templates select="duration"/> </duration> <price> <xsl:apply-templates select="price"/> </price> <instruction_language> <xsl:apply-templates select="instruction_language"/> </instruction_language> </xsl:template> <!-- Basis --> <xsl:template match="/"> <ou_schedule> <xsl:apply-templates select="//class"/> </ou_schedule> </xsl:template> <xsl:template match="class"> <xsl:copy> <xsl:call-template name="XPathList"/> </xsl:copy> </xsl:template> </xsl:stylesheet> |
好吧,本来可以以更紧凑的方式编写此转换的。但是,目的是将"具有XPATH列表以转换XML"的思想转换为代码。