tags:

views:

102

answers:

2

I am not sure whether the title clearly explains my problem, will try to include as much details I can.

I need to convert below xml to a properly formatted one using Xslt 1, so that I can deserialize it to a .net type.

Source XML

   <ax21:result type="test.ws.Result">
    <ax21:columnNames>fileName</ax21:columnNames>
    <ax21:columnNames>lockedState</ax21:columnNames>
    <ax21:columnNames>currentLockOwner</ax21:columnNames>
    <ax21:columnNames>UUID</ax21:columnNames>
    <ax21:resultData>Test1.doc</ax21:resultData>
    <ax21:resultData>true</ax21:resultData>
    <ax21:resultData>analyst</ax21:resultData>
    <ax21:resultData>f48f0450-9ecc-4a44-b063-898d9d72d112</ax21:resultData>
    <ax21:resultData>Test2.doc</ax21:resultData>
    <ax21:resultData>false</ax21:resultData>
    <ax21:resultData/>
    <ax21:resultData>f48f0450-9ecc-4a44-b063-898d9d72d112</ax21:resultData>
    <ax21:resultData>Test3.doc</ax21:resultData>
    <ax21:resultData>true</ax21:resultData>
    <ax21:resultData>analyst</ax21:resultData>
    <ax21:resultData>f48f0450-9ecc-4a44-b063-898d9d72d112</ax21:resultData>
    <ax21:resultData>Test4.doc</ax21:resultData>
    <ax21:resultData>false</ax21:resultData>
    <ax21:resultData/>
    <ax21:resultData>f48f0450-9ecc-4a44-b063-898d9d72d112</ax21:resultData>
   </ax21:result>

Target XML

<result>
    <item>
     <fileName>Test1.doc</fileName>
     <lockedState>true</lockedState>
     <currentLockOwner>analyst</currentLockOwner>
     <UUID>f48f0450-9ecc-4a44-b063-898d9d72d112</UUID>
    </item>
    <item>
     <fileName>Test2.doc</fileName>
     <lockedState>true</lockedState>
     <currentLockOwner>analyst</currentLockOwner>
     <UUID>f48f0450-9ecc-4a44-b063-898d9d72d112</UUID>
    </item>
    <item>
     <fileName>Test2.doc</fileName>
     <lockedState>true</lockedState>
     <currentLockOwner>analyst</currentLockOwner>
     <UUID>f48f0450-9ecc-4a44-b063-898d9d72d112</UUID>
    </item>
</result>

Can this be done using xslt? If yes, pls post a link or a sample xslt for me to try.

I am using .net 2.0, c#, XSLT 1.0

A: 

That's going to be really tough, if not impossible, to get done properly and reliably.

The trouble is: there's no indication of an index or type on the many and nodes as to what their type / position is - you have to more or less guess...

It would be a different story if the at least had an additional attribute, something like and and so forth - that way one might be able to somehow associate those together.

Is there any chance at all you could "massage" the original data a bit?? Otherwise, I'd probably say you'll have to load it in C# and parse it and do a lot of manual processing to get what you want.

Marc

marc_s
Not tough at all! And certainly, absolutely possible. XSLT is a powerful language that provides elegant solutions to such problems. One just needs to think :)
Dimitre Novatchev
I had similar thoughts, I was waiting for Dimitre's response. Anyways I have worked with the other team to get the xml in a more meaningful structure.
gk
Guys, if you want to see elegant XSLT solutions to some really challenging problems, look here: http://stackoverflow.com/questions/693596/elegant-examples-of-xslt/693953#693953
Dimitre Novatchev
+3  A: 

This is one fairly short and easy solution:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ax21="my:ax21" exclude-result-prefixes="ax21"
 >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <xsl:variable name="vCols" select="/*/ax21:columnNames"/>
  <xsl:variable name="vNumCols" select="count($vCols)"/>

    <xsl:template match="ax21:result">
     <result>
       <xsl:apply-templates select=
        "ax21:resultData[position() mod $vNumCols = 1]"
        />
     </result>
    </xsl:template>

    <xsl:template match="ax21:resultData">
      <item>
        <xsl:apply-templates mode="create" select=
         "(.|following-sibling::ax21:resultData)
                               [not(position() > $vNumCols) ]
         "/>
      </item>
    </xsl:template>

    <xsl:template match="ax21:resultData" mode="create">
      <xsl:variable name="vPos" select="position()"/>
    <xsl:element name="{$vCols[$vPos]}">
       <xsl:value-of select="."/>
       <xsl:if test="not(text())">
         <xsl:value-of select=
          "(.| preceding-sibling::ax21:resultData)
                      [position() mod $vNumCols = $vPos]
                         [text()]
                               [last()]
          "/>
       </xsl:if>
    </xsl:element>
    </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document:

<ax21:result type="test.ws.Result"
 xmlns:ax21="my:ax21"
>
    <ax21:columnNames>fileName</ax21:columnNames>
    <ax21:columnNames>lockedState</ax21:columnNames>
    <ax21:columnNames>currentLockOwner</ax21:columnNames>
    <ax21:columnNames>UUID</ax21:columnNames>
    <ax21:resultData>Test1.doc</ax21:resultData>
    <ax21:resultData>true</ax21:resultData>
    <ax21:resultData>analyst</ax21:resultData>
    <ax21:resultData>f48f0450-9ecc-4a44-b063-898d9d72d112</ax21:resultData>
    <ax21:resultData>Test2.doc</ax21:resultData>
    <ax21:resultData>false</ax21:resultData>
    <ax21:resultData/>
    <ax21:resultData>f48f0450-9ecc-4a44-b063-898d9d72d112</ax21:resultData>
    <ax21:resultData>Test3.doc</ax21:resultData>
    <ax21:resultData>true</ax21:resultData>
    <ax21:resultData>analyst</ax21:resultData>
    <ax21:resultData>f48f0450-9ecc-4a44-b063-898d9d72d112</ax21:resultData>
    <ax21:resultData>Test4.doc</ax21:resultData>
    <ax21:resultData>false</ax21:resultData>
    <ax21:resultData/>
    <ax21:resultData>f48f0450-9ecc-4a44-b063-898d9d72d112</ax21:resultData>
</ax21:result>

the wanted result is produced:

<result>
   <item>
      <fileName>Test1.doc</fileName>
      <lockedState>true</lockedState>
      <currentLockOwner>analyst</currentLockOwner>
      <UUID>f48f0450-9ecc-4a44-b063-898d9d72d112</UUID>
   </item>
   <item>
      <fileName>Test2.doc</fileName>
      <lockedState>false</lockedState>
      <currentLockOwner>analyst</currentLockOwner>
      <UUID>f48f0450-9ecc-4a44-b063-898d9d72d112</UUID>
   </item>
   <item>
      <fileName>Test3.doc</fileName>
      <lockedState>true</lockedState>
      <currentLockOwner>analyst</currentLockOwner>
      <UUID>f48f0450-9ecc-4a44-b063-898d9d72d112</UUID>
   </item>
   <item>
      <fileName>Test4.doc</fileName>
      <lockedState>false</lockedState>
      <currentLockOwner>analyst</currentLockOwner>
      <UUID>f48f0450-9ecc-4a44-b063-898d9d72d112</UUID>
   </item>
</result>

Explanation:

  1. For convenience the column names and their number are collected in the global variables $vCols and $vNumCols.

  2. We are applying templates to every N-th ax21:resultData element, where N mod $vNumCols = 1 . Every such element starts a new item.

  3. Every ax21:resultData element that will be the first in an item is matched by a template in "no-mode". THis simply creates the wrapping item element and applies to all current $vNumCols ax21:resultData elements another template -- in "create" mode.

  4. The template in "create" mode simply creates an element, whose name is the value of the n-th element in $vCols, where n is the position() of the current() node to which the template is being applied.

  5. Finally, if it happens that no value was supplied, we get (in backwards order) the latest non-emptyvalue for the same type of element.

Dimitre Novatchev
Thanks Dimitre!!. I was not sure whether this can be solved by XSLT, I only posted it here to get your opinion on this problem. Thanks again.
gk
On another note, the xml structure got changed since I posted this and its more correct and has an index as suggested by Marc. Your above answer will help me to create the template for the new one. Thanks again.
gk
@gk You are welcome! :) There are many problems that seem even "tougher" than this, that can be solved in an elegant way in XSLT. In a short period I have solved 60 Project Euler problems, and the solution to any of these (that could not be done with pen and pencil) was implemented in XSLT.
Dimitre Novatchev
+1 - excellent answer, and quite a nice piece of showing off what can be done in XSLT - great work!
marc_s