问题
I'm quite new to the XSLT world and need assistance with the following: A program takes the following string:
cn = 'James Bond' and (sn='Bon*' or givenName='Jam*')
and generates the following XML which is my input XML that I need to process using a stylesheet.
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<queryString>
<parameters>
<parameter id = "1">
<name>cn</name>
<value>James Bond</value>
<comparativeOperator>=</comparativeOperator>
<parens>
<leftParen>((</leftParen>
<rightParen>)</rightParen>
</parens>
</parameter>
<parameter id = "25">
<name>sn</name>
<value>Bon*</value>
<comparativeOperator>=</comparativeOperator>
<parens>
<leftParen>((</leftParen>
<rightParen>)</rightParen>
</parens>
</parameter>
<parameter id = "50">
<name>givenName</name>
<value>Jam*</value>
<comparativeOperator>=</comparativeOperator>
<parens>
<leftParen>(</leftParen>
<rightParen>)))</rightParen>
</parens>
</parameter>
</parameters>
<logicalOperators>
<operator id = "20">
<value>and</value>
<precedingParameterId>1</precedingParameterId>
<followingParameterId>25</followingParameterId>
</operator>
<operator id = "46">
<value>or</value>
<precedingParameterId>25</precedingParameterId>
<followingParameterId>50</followingParameterId>
</operator>
</logicalOperators>
</queryString>
Desired Output:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:filter>
<ns0:and>
<ns0:or>
<ns0:equalityMatch name="cn">
<ns0:value>James Bond</ns0:value>
</ns0:equalityMatch>
</ns0:or>
<ns0:or>
<ns0:approxMatch name="givenName">
<ns0:value>Jam*</ns0:value>
</ns0:approxMatch>
<ns0:approxMatch name="sn">
<ns0:value>Bon*</ns0:value>
</ns0:approxMatch>
</ns0:or>
</ns0:and>
</ns0:filter>
My existing xslt is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:ns0="urn:oasis:names:tc:DSML:2:0:core" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="ns0">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/queryString/logicalOperators/operator">
<ns0:filter>
<MyOp>
<xsl:value-of select="value"/>
</MyOp>
<xsl:for-each select="../../parameters/parameter">
<xsl:if test="comparativeOperator = '='">
<ns0:equalityMatch name="{name}">
<value>
<xsl:value-of select="value"/>
</value>
</ns0:equalityMatch>
</xsl:if>
</xsl:for-each>
</ns0:filter>
<!--/xsl:element-->
</xsl:template>
<xsl:template match="parens">
<xsl:element name="leftParensoutput">
<xsl:value-of select="leftParen"/>
</xsl:element>
<xsl:element name="rightParensoutput">
<xsl:value-of select="rightParen"/>
</xsl:element>
</xsl:template>
<xsl:template match="parameters/parameter">
<xsl:element name="FilterParameters">
<xsl:apply-templates select="parens"/>
<xsl:element name="queryfilterParameterElement">
<xsl:element name="id">
<xsl:value-of select="@id"/>
</xsl:element>
<xsl:element name="name">
<xsl:value-of select="name"/>
</xsl:element>
<xsl:element name="value">
<xsl:value-of select="value"/>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Current output:
<?xml version="1.0" encoding="UTF-8"?>
<FilterParameters>
<leftParensoutput>((</leftParensoutput>
<rightParensoutput>)</rightParensoutput>
<queryfilterParameterElement>
<id>1</id>
<name>cn</name>
<value>James Bon*</value>
</queryfilterParameterElement>
</FilterParameters>
<FilterParameters>
<leftParensoutput>((</leftParensoutput>
<rightParensoutput>)</rightParensoutput>
<queryfilterParameterElement>
<id>25</id>
<name>sn</name>
<value>Bon*</value>
</queryfilterParameterElement>
</FilterParameters>
<FilterParameters>
<leftParensoutput>(</leftParensoutput>
<rightParensoutput>)))</rightParensoutput>
<queryfilterParameterElement>
<id>50</id>
<name>givenName</name>
<value>Jam*</value>
</queryfilterParameterElement>
</FilterParameters>
<ns0:filter xmlns:ns0="urn:oasis:names:tc:DSML:2:0:core">
<MyOp>and</MyOp>
<ns0:equalityMatch name="cn">
<value>James Bon*</value>
</ns0:equalityMatch>
<ns0:equalityMatch name="sn">
<value>Bon*</value>
</ns0:equalityMatch>
<ns0:equalityMatch name="givenName">
<value>Jam*</value>
</ns0:equalityMatch>
</ns0:filter>
<ns0:filter xmlns:ns0="urn:oasis:names:tc:DSML:2:0:core">
<MyOp>or</MyOp>
<ns0:equalityMatch name="cn">
<value>James Bon*</value>
</ns0:equalityMatch>
<ns0:equalityMatch name="sn">
<value>Bon*</value>
</ns0:equalityMatch>
<ns0:equalityMatch name="givenName">
<value>Jam*</value>
</ns0:equalityMatch>
</ns0:filter>
I am dealing with multiple issues. Wasn't sure if I needed to split up the questions, but decided on presenting the whole issue.
Thanks in advance!
i) As I loop through the logicalOperators, how do I match the precedingParameterId to the under parameters.
ii) In the desired output, how do I create the node: - ie. dynamically add "ns0" AND get the "value" of parameter/operator
iii)) I'm not sure how to remove the extraneous FilterParameters element. If I remove the section , my output looks like:
<?xml version="1.0" encoding="UTF-8"?>**cnJames Bon*=(()snBon*=(()givenNameJam*=()))**<ns0:filter xmlns:ns0="urn:oasis:names:tc:DSML:2:0:core">
<MyOp>and</MyOp>
<ns0:equalityMatch name="cn">
<value>James Bon*</value>
</ns0:equalityMatch>
<ns0:equalityMatch name="sn">
<value>Bon*</value>
</ns0:equalityMatch>
<ns0:equalityMatch name="givenName">
<value>Jam*</value>
</ns0:equalityMatch>
</ns0:filter><ns0:filter xmlns:ns0="urn:oasis:names:tc:DSML:2:0:core">
<MyOp>or</MyOp>
<ns0:equalityMatch name="cn">
<value>James Bon*</value>
</ns0:equalityMatch>
<ns0:equalityMatch name="sn">
<value>Bon*</value>
</ns0:equalityMatch>
<ns0:equalityMatch name="givenName">
<value>Jam*</value>
</ns0:equalityMatch>
</ns0:filter>
Added logic:
The pretzel logic is as follows:
create a root node ;
for each queryString/logicalOperators/operator, create a node where operator is the value of logicalOperators/operator/value;
Inside this node, use logicalOperators/operator/precedingParameterId and followingParameterId to match them up with
queryString/parameters/parameter/parens/leftParen and rightParen;
if the pattern is the same, then get queryString/parameters/parameter/name and value and close with ns0:operator tag
This should get
<ns0:equalityMatch name="cn">
<ns0:value>James Bond</ns0:value>
</ns0:equalityMatch>
if they are not the same, create a node and for the logicalOperators/operator/precedingParameterId, get the corresponding queryString/parameters/parameter @id/name and value and close; for the logicalOperators/operator/followingParameterId, get the corresponding queryString/parameters/parameter @id/name and value and close; This should give:
<ns0:or>
<ns0:approxMatch name="givenName">
<ns0:value>Jam*</ns0:value>
</ns0:approxMatch>
<ns0:approxMatch name="sn">
<ns0:value>Bon*</ns0:value>
</ns0:approxMatch>
if queryString/parameters/parameter/value does not contain star use node equalityMatch else use node approxMatch
回答1:
Who designed this intermediate XML representation of the expression, and why did they do it this way? Is the syntax and semantics of this XML representation well defined, or is it just "specified by example"? Its method of indicating operator precedence by sequences of left- and right- parentheses is highly idiosyncratic. I would say it was designed by someone with very little knowledge or experience of parser design.
Given a free choice, I would personally start from the original free-form expression rather than from this intermediate XML representation. Of course, I would want to know the full grammar of the expression language - at the moment we only have one example expression to work from. It looks like a fairly simple grammar, and parsing simple grammars in XSLT is not difficult if you know the theory. There are good examples of parser-writing in XSLT from Dimitre Novatchev and from Gunther Rademacher.
Unfortunately it's fairly clear from your post that you don't know the theory, and so I'm in the difficult position of trying to suggest a way forward that's constrained by what we know about your level of knowledge and experience.
So, since the lexical analysis phase of parsing has already been done, let's take the problem as given and see what we can do with your intermediate XML representation. The only difficult part of the problem (for me: there might be other parts that are difficult for you) is to work out how to use the left- and right parens info to construct the hierarchical expression tree in your output.
If we take the three "parameters" in your expression (which would usually be called "terms") we can assign each of them a level number: 1, 2, 2 respectively. This represents the depth of the term as it appears in the final expression tree. The level of each term can be deduced from your intermediate input by counting parentheses: the level of a term is
the sum of the number of left parentheses in this and preceding terms
minus
the sum of the number of right parentheses in preceding terms
minus 1
Translating that computation into XSLT terms depends strongly on whether you are using XSLT 1.0 or 2.0, which you haven't actually said. If we assume 2.0, it's
sum((.|preceding-sibling::parameter)/parens/string-length(left-paren))
-
sum(preceding-sibling::parameter/parens/string-length(right-paren))
-1
Now, once you've got the level numbers, constructing a tree is a "well-known" problem, a classic exercise in using grouping constructs. Unfortunately it's well beyond beginner XSLT level, but here goes.
Given a sequence of elements with level numbers:
<a level="1"/>
<b level="2"/>
<c level="3"/>
<d level="3"/>
<e level="2"/>
we can turn them into a tree structure
<a><b><c/><d/></b><e/></a>
using recursive grouping as follows. We write a template that does one level of grouping, and then calls itself recursively to do the next level:
<xsl:template name="grouping">
<xsl:param name="input" as="element()*"/>
<xsl:if test="exists($input)">
<xsl:variable name="level" select="$input[1]/@level"/>
<xsl:for-each-group select="$input"
group-starting-with="*[@level=$level]">
<xsl:copy>
<xsl:call-template name="grouping">
<xsl:with-param name="input"
select="current-group()[position() gt 1]"/>
</xsl:call-template>
</xsl:copy>
</xsl:for-each-group>
</xsl:if>
</xsl:template>
Again, that's using XSLT 2.0. A solution using XSLT 1.0 is going to be much, much harder.
I've given you an outline here and I appreciate that with your level of XSLT experience, fleshing it out is going to be quite hard work. However, I hope you now have a better understanding of the task ahead.
来源:https://stackoverflow.com/questions/34301195/xslt-transformation-of-boolean-expressions