XML to XML conversion based on the value of the previous sibling and ancestor attribute for the parent
Note. Edited to make elements and attribute values visible (they were not visible before because I used angle brackets) and to provide a complete sample XML source file.
I am trying to create XSL to perform complex XML-XML transformation.
I have an XML that has some text elements <a:t>
that have different paths that start with /p:sld
or /p:notes
but always end with
/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:t
.
These elements <a:t>
must be converted to different target XML elements based on how the path begins and the value of the (sometimes present) attribute preceding the sibling <a:pPr>
on <a:r>
, which in turn is the parent of <a:t>
.
Here's a minimal version of the original XML, for example: http://pastie.org/9559309
Here are the xpaths of the various elements <a:t>
and the target XML for them. Edit: I added a new item at the start of items whose paths start/p:notes
- xpath
/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:t
-<Body>
- xpath
/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:t
(tag<a:r>
preceded by<a:pPr lvl="1">
)) to<Bulleted>
- xpath
/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:t
(tag<a:r>
preceded by<a:pPr lvl="2">
)) to<Bulleted2>
- xpath
/p:notes/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:t
-<Body>
- xpath
/p:notes/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:t
(tag<a:r>
preceded by sibling<a:pPr lvl="1">
) to<Body>
- xpath
/p:notes/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:t
(tag<a:r>
preceded by sibling<a:pPr lvl="2">
) to<Bulleted>
- xpath
/p:notes/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:t
(tag<a:r>
preceded by sibling<a:pPr lvl="3">
) to<Bulleted2>
Notice how the start of the path interaction is present and the attribute value <a:pPr>
, when <a:pPr>
present, that determines which xml element is rendered. For example:
The immediately preceded <a:t>
parent should create if the original xpath starts
But if the original path starts with a tag with everything else, then it should be in the output.<a:r>
<a:pPr "lvl=1">
<Bulleted>
/p:sld
/p:notes
<a:t>
<Body>
Finally, I also need to convert a couple of elements near the root xpath /p:notes
to <PageBreak>
and xpath /p:sld
to <PageBreak>
.
The output XML should look like this. Edit: This now matches the content of the original xml.
<Document>
<PageBreak></PageBreak>
<bodytext>Activity 1:</bodytext>
<bodytext>Services Platform</bodytext>
<bodytext>Objective :</bodytext>
<Bulleted>The objective of this activity is to identify which services each node supports</Bulleted>
<bodytext>Description :</bodytext>
<Bulleted>This will be an individual activity. Using the information we just covered and the drawing on the next page fill in the table with the information requested by your Instructor</Bulleted>
<bodytext>IG: Mandatory individual activity. Time: 5 – 10 min.</bodytext>
<PageBreak></PageBreak>
<bodytext>2</bodytext>
<bodytext>Activity 1: </bodytext>
<bodytext>Services Platform</bodytext>
<bodytext>To complete this activity, use the following material:</bodytext>
<Bulleted>The drawing and table on the next page, along with the information provided by your Instructor.</Bulleted>
</Document>
<PageBreak>
- empty element, elements <bodytext>
and <Bullet>
contain text from the corresponding <a:t>
in the source XML, all other elements from the source XML are stripped.
UPDATE: I wrote some xsl that seems to work on the content of each a: t element in the input xml and put the corresponding output element name around it in the output. The body was changed to bodytext, so I don't get any problems with the body being a reserved html word.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
<xsl:template match="/">
<xsl:for-each select="//a:t/">
<xsl:choose>
<xsl:when test="(count(ancestor::p:sld) > 0) and (count(ancestor::a:r[preceding-sibling[1]@lvl='1']) > 0">
<bulleted><xsl:value-of select="."/></bulleted>
</xsl:when>
<xsl:when test="(count(ancestor::p:sld) > 0) and (count(ancestor::a:r[preceding-sibling[1]@lvl='2']) > 0">
<bulleted2><xsl:value-of select="."/></bulleted2>
</xsl:when>
<xsl:when test="(count(ancestor::p:notes) > 0) and (count(ancestor::a:r[preceding-sibling[1]@lvl='2']) > 0">
<bulleted><xsl:value-of select="."/></bulleted>
</xsl:when>
<xsl:when test="(count(ancestor::p:notes) > 0) and (count(ancestor::a:r[preceding-sibling[1]@lvl='3']) > 0">
<bulleted><xsl:value-of select="."/></bulleted>
</xsl:when>
<xsl:otherwise>
<bodytext><xsl:value-of select="."/></bodytext>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I am getting syntax errors from Transform related to which a:t
prefix is used, which I am not sure how to fix these errors.
I'm also not sure how to tell the XSL Transform Processor to put the page output in the output where each p.sld or p.notes element appears in the input and they appear in the right place. It seems that I have to make an external command "for-each / document / p.sld or /document/p.notes" which
- outputs a couple of tags
<pagebreak></pagebreak>
- then triggers another one internally for each
a:t
, as in the above code, but limited to the scope of the itemp.sld
orp.notes
currently selected external for each - then closes the outside for each command sending the processor to find the next
p.sld
orp.notes
in the original xml.
Thanks for any help you can provide.
source to share
No one has answered this question yet
Check out similar questions: