471,078 Members | 828 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,078 software developers and data experts.

Match an attribute value in a set of possible values

Hi All,

given the source document:

<?xml version="1.0" encoding="UTF-8"?>
<root>
<child>
<test id="1" name="first child"/>
</child>
<child>
<test id="2" name="second child"/>
</child>
<child>
<test id="3" name="third child"/>
</child>
</root>

I would suppress the the child elements that contain a test element
with the value of the id attribute in a list of known ids; for
instance, assuming that the list of ids to remove is {1,3}, I would
obtain the document:

<?xml version="1.0" encoding="UTF-8"?>
<root>
<child>
<test id="2" name="second child"/>
</child>
</root>

where only the second child element is retained.

I'm only able to suppress a single child element using the following
XSL transform;

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
<xsl:output method="xml" indent="yes" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="child[test[@id='1']]"/>
</xsl:stylesheet>

Is it possible to match and suppress multiple child elements whose ids
are contained in a given list ?

Since I'm invoking the transform from Java, I would like to set a
template parameter to the list of ids to be removed; e.g. as comma
delimited list, and write the last match condition as something like
this:

....
<xsl:param name="idToRemove" select="default" />
<xsl:template match="child[test[@id in $idsToRemove]]"/>
....

Note that I'm using XSLT 1.0, so I cannot leverage the enhancements of
XSLT 2.0, but I'm wondering if there is some trick to do the job; for
instance injecting the list of ids to remove into the same XSLT
document and then iterating over them and suppressing any matching
child element from the source document... maybe something like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:data="http://sample.array.parameters/data" version="1.0">
<xsl:output method="xml" indent="yes" />
<data:parameters>
<id>1</id>
<id>3</id>
</data:parameters>
<xsl:variable name="parameters-root"
select="document('')/*/data:parameters"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="child[test[@id in $parameters-root]]"/>
</xsl:stylesheet>

Thanks a lot for any suggestion

Regards,

Patrizio

Sep 1 '06 #1
5 1662


patrin wrote:
<xsl:template match="child[test[@id='1']]"/>
Well an or is possible in XPath 1.0

<xsl:template match="child[test[@id='1' or @id = '3']]"/>
--

Martin Honnen
http://JavaScript.FAQTs.com/
Sep 1 '06 #2

Martin Honnen wrote:
patrin wrote:
<xsl:template match="child[test[@id='1']]"/>

Well an or is possible in XPath 1.0

<xsl:template match="child[test[@id='1' or @id = '3']]"/>
--

Martin Honnen
http://JavaScript.FAQTs.com/
Thanks for the quick replay, but the list of ids to match is dynamyc
and I know it only from Java code, where it is stored into a String
arrray:

String[] idsToRemove = {1,3,...};

and I would like to pass it to the Transformer, invoking the method:

setParameter("idsToRemove", idsToRemove);

so that the XSLT should remove all the child elements whose test
subelement has an id with any of the values contained in the
idsToRemove array.

Is it possible to do this with XSLT 1.0 ? or can you suggest an
alternative way to solve the problem ?

Thanks again for your help

Regards,

Patrizio

Sep 1 '06 #3
patrin wrote:
setParameter("idsToRemove", idsToRemove);
Well, you're unlikely to be able to pass in an array of strings and do
anything useful with it except (maybe) pass it back out to an extension
function. Xalan, for example, will treat that as a "non-XSLT type".

I'd suggest reformatting the data as a single string, which most
processors should treat as an XSLT string value, and then doing string
manipulation to check if the ID is contained in that list. I did a
quick-and-dirty version of that in my styling-stylesheets article; since
it was just an illustration I didn't make it highly robust but you could
make it stronger by checking delimiters.

Alternatively, some processors (again, Xalan is one of these) may let
you pass in a DOM node and have XSLT treat it as a node-set. If you
construct a tree where one element has each of the values, you could
then do an XSLT node-set comparison (does any node in one set equal any
node in the other set). The stylesheet would be cleaner, but the
preparation would be more complex. I'm not sure how portable this
approach would be; the TrAX APIs don't make any particular promises
about how non-string parameters will be processed.

Alternatively: Build that nodes-I'm-interested-in XML document as text,
and put it somewhere that the stylesheet can access via the document()
function. Less efficient, but it gives you the simple "does anything
equal" syntax in the stylesheet, and it may be more portable than
relying on the API to do something useful with Nodes.
--
Joe Kesselman / Beware the fury of a patient man. -- John Dryden
Sep 1 '06 #4


patrin wrote:

Thanks for the quick replay, but the list of ids to match is dynamyc
and I know it only from Java code, where it is stored into a String
arrray:

String[] idsToRemove = {1,3,...};

and I would like to pass it to the Transformer, invoking the method:

setParameter("idsToRemove", idsToRemove);

so that the XSLT should remove all the child elements whose test
subelement has an id with any of the values contained in the
idsToRemove array.

Is it possible to do this with XSLT 1.0 ?
I guess there are two approaches, one that might be processor dependent
where you need to look at the documentation of the XSLT processor what
kind of types you can pass in with setParameter:

The setParameter method
<http://java.sun.com/j2se/1.5.0/docs/api/javax/xml/transform/Transformer.html#setParameter(java.lang.String,%20 java.lang.Object)>
as the second argument takes any object so it might well be that it
works to pass in a Source (e.g. DOMSource or SAXSource or StreamSource)
there as well. But details depend on the XSLT processor, I don't think I
have tried that so far with Saxon or Xalan.
The second approach would take the XSLT stylesheet as an XML document
and change it as needed depending on your idsToRemove before the
stylesheet is used for the transformation.
--

Martin Honnen
http://JavaScript.FAQTs.com/
Sep 1 '06 #5
As others have commented, there are different way to structure the
externally passed parameter or even to modify dynamically the stylesheet, so
that passing a parameter will not be necessary.

But your initial question was how to find that the @id attribute of the
current element is part of ids list, encoded as a coma-delimited string. One
way to determine this is using the following XPath 1.0 expression:

contains($idList, concat(' ', @id, ','))

This will work if every id in the list starts with at least one space and is
followed immediately by a coma. The global parameter itself will have to be
produced and passed to the stylesheet by an implementation-dependent
component -- as this varies for different XSLT processors, one needs to
study the documentation of the specific XSLT processor used in order to
understand how this should be done.

Certainly there are ways to perform more precise tokenization (one example
is the tokenization implemented in the FXSL library), but this simple
solution is probably good just for a start.

Cheers,
Dimitre Novatchev

"patrin" <pa****************@googlemail.comwrote in message
news:11**********************@m73g2000cwd.googlegr oups.com...
Hi All,

given the source document:

<?xml version="1.0" encoding="UTF-8"?>
<root>
<child>
<test id="1" name="first child"/>
</child>
<child>
<test id="2" name="second child"/>
</child>
<child>
<test id="3" name="third child"/>
</child>
</root>

I would suppress the the child elements that contain a test element
with the value of the id attribute in a list of known ids; for
instance, assuming that the list of ids to remove is {1,3}, I would
obtain the document:

<?xml version="1.0" encoding="UTF-8"?>
<root>
<child>
<test id="2" name="second child"/>
</child>
</root>

where only the second child element is retained.

I'm only able to suppress a single child element using the following
XSL transform;

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
<xsl:output method="xml" indent="yes" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="child[test[@id='1']]"/>
</xsl:stylesheet>

Is it possible to match and suppress multiple child elements whose ids
are contained in a given list ?

Since I'm invoking the transform from Java, I would like to set a
template parameter to the list of ids to be removed; e.g. as comma
delimited list, and write the last match condition as something like
this:

...
<xsl:param name="idToRemove" select="default" />
<xsl:template match="child[test[@id in $idsToRemove]]"/>
...

Note that I'm using XSLT 1.0, so I cannot leverage the enhancements of
XSLT 2.0, but I'm wondering if there is some trick to do the job; for
instance injecting the list of ids to remove into the same XSLT
document and then iterating over them and suppressing any matching
child element from the source document... maybe something like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:data="http://sample.array.parameters/data" version="1.0">
<xsl:output method="xml" indent="yes" />
<data:parameters>
<id>1</id>
<id>3</id>
</data:parameters>
<xsl:variable name="parameters-root"
select="document('')/*/data:parameters"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="child[test[@id in $parameters-root]]"/>
</xsl:stylesheet>

Thanks a lot for any suggestion

Regards,

Patrizio

Sep 2 '06 #6

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

1 post views Thread by Ravi | last post: by
5 posts views Thread by Gerald Aichholzer | last post: by
5 posts views Thread by Soledad Vel | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.