Tuesday, August 27, 2013

Dynamic XML Transformation in JAVA


I couldn't find one decent library in Java which transforms XMLs using XPath mappings. There are several stack over flow threads thread1,thread2 ended up using either XSLT or any Object mapping frameworks.So i've developed a basic utility class which accomplishes this task without using  any third party libraries .

Requirement:

      Need to form Target XML as shown below given Source XML & XPath mappings.
     













Constraints : No XSLT,No XSD, No JAXB


Solution:

  Complete code is available in my github repository,  feel free to fork it to contribute :). Complete transformation can be by following these 3 steps.

 Step 1: Converting generic XPaths to Complete XPaths


    Input of this step is a Map of generic XPaths as shown,
 /TargetOrder/OrderId = /SourceOrder/@id  
 /TargetOrder/TargetOrderDate = /SourceOrder/SourceOrderDate  
 /TargetOrder/TargetItems/Item[a]/@id = /SourceOrder/SourceItems/SourceItem[a]/@id  
 /TargetOrder/TargetItems/Item[a]/@desc = /SourceOrder/SourceItems/SourceItem[a]/ItemDesc  

To get the complete XPaths we need to run source XPaths against the source XML so that array size etc.. cab be identified. This complete  operation is done in the method generateCompleteXpaths() of class DynamicXMLTransformer.java. Final Map after going through this phase looks like,

 /TargetOrder/TargetItems/Item[1]/@desc = Nexus 4  
 /TargetOrder/TargetItems/Item[2]/@desc = Nexus 10  
 /TargetOrder/TargetItems/Item[1]/@id = 1111111  
 /TargetOrder/TargetItems/Item[2]/@id = 222222  
 /TargetOrder/TargetOrderDate = 2013-08-27T11:00:00  
 /TargetOrder/TargetItems/Item[1]/Taxes/Tax[1]/@type = SalesTax  


 Step 2: Generation of Target DOM based on previous step XPaths


      Only tricky part of this algorithm is to generate target DOM based on the target XPaths.
      This operation is done in the method createTargetDocument().This achieved just iterating
      individual  XPaths and creating childNodes under the parent elements.

Step 3: Populating Target DOM with values from the Map


         Once target DOM is ready we will fire XPath on it and get the NodeList, based on the Node
          type if  it is  an attribute we will call .setNodeValue() & if it is Element call .setTextContent()
          as shown,

  if (nodes.item(0) instanceof Attr) {  
     ((Attr) nodes.item(0)).setNodeValue(value);  
    } else if (nodes.item(0) instanceof Element) {  
     ((Element) nodes.item(0)).setTextContent(value);  
    }  

Thats it convert this target DOM to String and you are ready to go :) :)

For running complete code fork the git repository
(https://github.com/TheGanesh/DynamicXMLTransformer) & run the main method in the Main.java.

Happy coding .....


Thank you,
Ganesh Kandisa,
kandisa.ganesh@gmail.com

8 comments:

  1. Good job, Ganesh....

    I have 2 questions in here. Pardon me, I am new to this.

    1. What do the elements in [] represent in here i.e., a, b or numericals?

    2. What are the advantages of using this approach to than using XSLT?

    http://docs.oracle.com/cd/B19306_01/appdev.102/b14252/adx_j_xslt.htm#CHDHGEBE

    ReplyDelete
  2. 1. Node having [a] indicates its an array & will be replaced with exact index after running against source XML i.e that time we will come to know the size. (converting that [a] to [1],[2] is the Step 1 process above)

    2. Here mappings are dynamic i.e the entries of map(XPaths) comes from a program or in runtime,so XSLT not an option here.
    XSLT needs xsl mappings before starting transformation.

    ReplyDelete
  3. Thank you ... Ganesh, I appreciate that.

    ReplyDelete
  4. hey I have one issue using your code i have ~7-8k xpath to be added in an xml its taking lot of time almost 15 seconds. Can it be minimized

    ReplyDelete
  5. Hi Vikky, this code is written for flexibility as you can see it is running in O(m ^ n) where m is number of xpaths & n depth of the xml.
    And also using DOM API which is not efficient either, so to get little performace gain you can use Java object mapping frameworks & use JAXB objects for source and target objects but it is no more dynamic as you need to update JAXB objects if XML schema changes.

    ReplyDelete
  6. This comment has been removed by the author.

    ReplyDelete
  7. Hi Ganesh,
    Could you please help me to create same scenario using Dozer.

    /TargetOrder/TargetItems/Item[1]/@desc = Nexus 4
    /TargetOrder/TargetItems/Item[2]/@desc = Nexus 10
    /TargetOrder/TargetItems/Item[1]/@id = 1111111
    /TargetOrder/TargetItems/Item[2]/@id = 222222
    /TargetOrder/TargetOrderDate = 2013-08-27T11:00:00
    /TargetOrder/TargetItems/Item[1]/Taxes/Tax[1]/@type = SalesTax

    ReplyDelete
  8. This blog approach is Dynamic but if you say Dozer you are talking about complete static way of mapping/transforming object. Please ignore this blog and refer Dozer documentation :)

    ReplyDelete