Reputation: 43
I'm fairly new to XML and XSL Stylesheets, and I've been tasked with creating a stylesheet for one of our clients. I have already created a stylesheet that outputs an XML in the following format:
<Trip TripType="Normal">
<Plan BeginTime="2011-08-13T10:00:00" UserDefinedTripID="777" UserDefinedRouteID="777">
<PlanStop ArrivalTime="2011-08-13T15:30:00" ArrivalLock="true" SiteID="1" PassThru1="test1" PassThru2="test2" PassThru3="test3" PassThru4="test4">
<PlanNote Line1="Freeform Text" Line2="Line2" Line3="Line3" />
<PlanCargo Duration="60" BillID="" Weight="100" Units="100.0" XUnitTypeID="10" Action="Pick" />
<PlanNote Line1="Freeform Text" Line2="Line2" Line3="Line3" />
<PlanCargo Duration="60" BillID="" Weight="100" Units="100.0" XUnitTypeID="12" Action="Pick" />
</PlanStop>
</Plan>
</Trip>
I need to take the output and insert the contents into an attribute within the Trip element to look like this:
<Trip TripID="-1" CurrentRevisionNumber="1" IsDispatch="1" IsActive="0"
IsComplete="0" OrganizationID="4"
TripData="<Trip TripType="Normal">
<Plan BeginTime="2011-08-13T10:00:00" UserDefinedTripID="777"
UserDefinedRouteID="777">
<PlanStop ArrivalTime="2011-08-13T10:00:00" ArrivalLock=& quot;true" SiteID="1" PassThru1="test1" PassThru2=& quot;test2" PassThru3="test3" PassThru4="test4">
<PlanCargo Duration="45" BillID="" Weight=& quot;100" Units="100.0" XUnitTypeID="9" Action="Pick" />
</PlanStop> />
So in other words, I need to take an existing XML output and put it in an attribute while performing some character transformations.
Yes, it is extremely ugly, but this is how they want it. I was thinking of making another XSL that will copy over the XML output from the original XSL transformation and place it in an attribute while converting <, >, ", etc into < , > , " , etc (not sure what they're called).
I've scoured the internet for solutions, but I can't seem to find any that are quite like this (I'd imagine that's because this is a ridiculous request). I can provide my original XSL if necessary, but I'd rather not change it, if possible.
Thanks in advance!
Upvotes: 4
Views: 466
Reputation: 243529
I would save myself time, effort and depression from others' stupidity and will implement this with a tiny transformation that uses a tiny extension function.
Here is example using the .NET XslCompiledTransform XSLT processor:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:my="my:my" exclude-result-prefixes="msxsl my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<t stupid="{my:stringize(.)}"/>
</xsl:template>
<msxsl:script language="c#" implements-prefix="my">
public string stringize(XPathNavigator doc)
{
return doc.InnerXml;
}
</msxsl:script>
</xsl:stylesheet>
When applied on any XML document, it produces the top element with a stupid
attribute that contains the escaped representation of the body of the top element.
For example, when the transformation is applied on this XML document:
<t>
<a>
<b x="y">
<!-- Comment here -->
<?aPI ?>
</b>
</a>
</t>
the wanted result is produced:
<t stupid="
 <a>
 <b x="y">
 <!-- Comment here -->
 <?aPI?>
 </b>
 </a>
" />
A transformation implementing the same idea most probably can be written for almost every XSLT processor with an extension function that itself can be written in variety of programming languages.
Upvotes: 4
Reputation: 163458
Basically, you need to serialize the XML, capture the result of the serialization as a string, and then write an attribute with this string as its value. There's no direct way of doing this in XSLT 1.0 or XSLT 2.0. XSLT 3.0 has a function fn:serialize() designed to meet this requirement, and it's implemented in recent versions of Saxon. (Earlier versions of Saxon introduced the idea with a saxon:serialize() extension function. You may find similar functions in other products, or you may be able to write your own.) Implementing a serializer in XSLT itself is of course possible, as others have suggested; it's just rather tedious.
Upvotes: 2
Reputation: 28004
An XSLT stylesheet for serializing XML as escaped text can be found at http://code.google.com/p/httpfox/source/browse/trunk/chrome/content/XMLPrettyPrint.xsl?r=3 It outputs HTML, but you can easily adapt it to output XML, mostly by removing unneeded stuff, like expanders.
The only tricky part may be getting the XML output from your existing XSL as input to the new templates. If you implement the latter as a separate stylesheet, or if you use XSLT 2.0, this will be no problem. Otherwise, you may need to use the node-set()
extension function.
There are several aspects of the above that could be elaborated on. Holler if you get stuck! HTH.
P.S. Actually, since I started this post, @Sean posted his answer, and it's much easier to use than adapting the above stylesheet.
Upvotes: 2
Reputation: 12729
I have to agree with other commenters, that inserting an XML document as lexical XML within an attribute is completely nutty. The owners of the data will lose so much power and flexibility and gain nothing in return. However, if you really have no choice, then commit suicide (joke). If this option is unpalatable for you, then you could serialise your inner XML with templates like so ...
<xsl:template match="*" mode="serialise">
<xsl:value-of select="concat('<',local-name(),' ')">
<xsl:apply-templates select="@*" mode="serialise">
<xsl:value-of select="' >'">
<xsl:apply-templates select="*|text()" mode="serialise">
<xsl:value-of select="concat('</',local-name(),' >')">
</xsl:template>
<xsl:template match="@*" mode="serialise">
<xsl:value-of select="concat(local-name(),'="',.,'" ')">
</xsl:template>
<xsl:template match="text()" mode="serialise">
<xsl:value-of select=".">
</xsl:template>
Take the root element of the inner structure that you want to encoded as lexical XML and invoke <xsl:apply-templates> with @mode="serialise". Capture the output of this invocation into a string variable. You can then insert the contents of the string variable in what-ever attribute in the result tree you like.
These simple templates do not process comments(), processing-instructions() and DocTypes. Also, as per XDM (contrary to DOM), adjacent text type nodes (text, CDATA, character references etc.) will be merged. Also this does not work with namespaces.
Upvotes: 3