views:

54

answers:

3

I want an XML stylesheet (XSLT) that will put the attributes of a few, specific, child nodes one-to-a-line. What is the XSLT for this?

I recently asked a related question that someone offered a stylesheet to solve but their stylesheet didn't work for some reason, and I am curious why -- the attributes simply didn't end up one-per-line.

By way of example, my XML might look like this:

<MyXML>
    <NodeA>
       <ChildNode value1='5' value2='6' />
    </NodeA>
    <NodeB>
       <AnotherChildNode value1='5' value2='6' />
    </NodeB>
    <NodeC>
       <AnotherChildNode value1='5' value2='6' />
    </NodeC>
</MyXML>

And I want a stylesheet that will expand all NodeA's and NodeB's but not NodeCs and make it look like this:

<MyXML>
    <NodeA>
       <ChildNode 
          value1='5' 
          value2='6' />
    </NodeA>
    <NodeB>
       <AnotherChildNode 
          value1='5' 
          value2='6' />
    </NodeB>
    <NodeC>
       <AnotherChildNode value1='5' value2='6' />
    </NodeC>
</MyXML>
+1  A: 

I would say XSLT is not the right tool for this job.

XSLTs goal is to transform the data from one format into another.

You can achieve to output newlines for "pretty printing" but you should keep in mind, that this is always a kind of a "hack".

Is there a reason for using XSLT and not $PreferedScriptingLanguage?

echox
Technically, XSLT's goal is to transform an input tree into an output tree. How that tree is serialized into a document is largely outside the scope of the XSLT specification.
fpmurphy
@echox: I was under the impression that XSLT didn't care what it wrote out, as long as it INPUT XML. Can't you just spit out arbitrary text from an XSLT?
Scott Stafford
sure you can, but you can also beat a nail into the wall with a shoe. Sorry couldn't think of a better analogy at the moment ;-)You can achive this, but it will result in really ugly, unreadable code, which no one would want to maintain (see the solution of @tomalak).The XSLT Spec says in the first sentence: "[..]which is a language for transforming XML documents into other XML documents."http://www.w3.org/TR/1999/REC-xslt-19991116
echox
+1  A: 

XML wise, these are completely identical. So transforming them with XSLT will not change them.

So you might try to

  1. Abuse some XSLT processor (with an identity XSLT transformation) as a "pretty" printer.

  2. Get or write a special pretty printer.

  3. Leave the XML as it is, and adapt your next step in the processing chain to parse the XML instead of parsing values on lines.

I would favour 3.

ndim
For #1, see my answer. I'm voting for this one since personally, I would favor #3 as well.
Tomalak
@ndim: I'm not writing an XML parser (I'm not insane!). It's a human-readability issue. The XML format we've been handed uses 25-100 attributes on a few key nodes for configuration settings, and it's unreadable, barely-diffable by winmerge, and hard to edit. Much more manageable by the human when it's one-to-a-line.My understanding was that XSLT doesn't require the OUTPUT to be XML, is that wrong? From wikipedia (though it is a bit confusing to me) "The new document may be serialized (output) by the processor in standard XML syntax or in another format, such as HTML or plain text."
Scott Stafford
You would not need to write a parser (any existing parser should do) - you would need to write a serializer. So if you took a standard SAX parser, wrote some code to track the current context to determine where you are in the document, and then adapted the standard serializer to insert a few linebreaks... that should work. However, I would argue that with 100 attrs for one element that will still be quite unreadable for humans.
ndim
+1  A: 

Hm... If (and only if) the input XML already is indented correctly, you can get away with this:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
> 
  <xsl:output encoding="utf-8" />

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="NodeA/*|NodeB/*">
    <xsl:variable name="spc" select="preceding-sibling::text()[1]" />
    <xsl:value-of select="concat('&lt;', name())" disable-output-escaping="yes" />
    <xsl:for-each select="@*">
      <xsl:value-of select="concat($spc, '    ', name(), '=&quot;', ., '&quot;')" />
    </xsl:for-each>
    <xsl:choose>
      <xsl:when test="node()">
        <xsl:value-of select="'&gt;'" disable-output-escaping="yes" />
        <xsl:apply-templates select="node()" />
        <xsl:value-of select="concat('&lt;/', name(), '&gt;')" disable-output-escaping="yes" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="' /&gt;'" disable-output-escaping="yes" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

But to be honest, I consider this exactly the ugly hack that @echox talks about.

The output ouf the above would be:

<MyXML>
    <NodeA>
       <ChildNode
           value1="5"
           value2="6" />
    </NodeA>
    <NodeB>
       <AnotherChildNode
           value1="5"
           value2="6" />
    </NodeB>
    <NodeC>
       <AnotherChildNode value1="5" value2="6"></AnotherChildNode>
    </NodeC>
</MyXML>
Tomalak
upvote for special brainfuck solution :-D
echox
@echox: I do many XSLT questions, so I have a certain tolerance ;) towards it. Still, things like this make my eyes bleed. Nevertheless, here's a similar one I made a few days ago: http://stackoverflow.com/questions/2638367/how-do-i-emit-escaped-xml-representation-of-a-node-in-my-xslts-html-output/2652733#2652733 :-D
Tomalak