cancel
Showing results for 
Search instead for 
Did you mean: 

XML transformation issue

Former Member
0 Kudos

Hi all,

I have a scenario where I have an XML which needs to be transformed using a preprocessor to another transformation template. This in turn will be used to validate other XMLs.

Here is my code:

public class Validator
{

  /** DOM representation of the generated validator */
  private DOMResult domValidator = null;

  /** generated xsl:messages from the preprocessor */
  private Vector warnings = new Vector();

  /**
   * Initializes a new Validator object from the given
   * schema, the preprocessor and external parameters. This method
   * constructs a DOM instance of the validating stylesheet.
   */
  private void init(Source schema, Source preprocessor, 
                    Properties params)
    throws TransformerException,
           TransformerConfigurationException,  
           ParserConfigurationException
  {
    warnings = new Vector();
    TransformerFactory trfactory = TransformerFactory.newInstance();
    Transformer transformer = trfactory.newTransformer(preprocessor);
    Listener listener = new Listener();
    transformer.setErrorListener(listener);

    if (params != null) {
      Enumeration paramsEnum = params.propertyNames();
      while (paramsEnum.hasMoreElements()) {
        String name = paramsEnum.nextElement().toString();
        transformer.setParameter(name, params.getProperty(name));
      }
    }

    domValidator = new DOMResult();
    transformer.transform(schema, domValidator);
  }

  /**
   * Performs validation of the passed xml data.
   *
   * @param xmlsrc The XML data to be validated.
   * @return A Result object which represents the result
   *         of the validation.
   */
  public Result validate(Source xmlsrc) 
    throws TransformerException,
           TransformerConfigurationException,  
           ParserConfigurationException
  {
    TransformerFactory trsfactory = TransformerFactory.newInstance();
    Transformer validator =
      trsfactory.newTransformer(new DOMSource(domValidator.getNode()));
    DOMResult result = new DOMResult();
    validator.transform(xmlsrc, result);

    return new Result(result, getWarnings());
  }

  /**
   * The Listener class which catches messages
   */
  private class Listener implements javax.xml.transform.ErrorListener
  {

    public void warning(TransformerException e) 
    {
      warnings.add(e.getMessage());
    }

    public void error(TransformerException e)
      throws TransformerException
    {
      throw e;
    }

    public void fatalError(TransformerException e)
      throws TransformerException
    {
      throw e;
    }
  }



  /**
   * Returns an array of generated messages which appeared while 
   * transforming the schema.
   */
  public String[] getWarnings()
  {
    int size = warnings.size();
    String[] ret = new String[size];

    for (int i=0; i<size; i++)
      ret<i> = (String)warnings.get(i);
  
    return ret;
  }

}

The same code works perfect in case of a java application on the system. But when I import this to a PAR and run it as a component, it doesn't transform. Also, there are no errors reported.

On further testing I see that the result of the first transform is perfect, but the second transform is not. I saved the result of the first transform in a file on my system and tested the second transform using IE. This gives the correct output.

Is there any problem with the classloading or something that I need to take care of when running on the server?

Accepted Solutions (1)

Accepted Solutions (1)

Former Member
0 Kudos

Nirav,

Just wondering: does it works if you add Templates to the picture (insead of creating transformer directly). Or if you clone domValidator and use it as source for transformer?

VS

Former Member
0 Kudos

Hi,

I've tried both Valery...doesn't help.

Ok, for all who face this issue, here goes. You dont need Xerces cause thats only for parsing and the SAP APIs seem ok for this. For transforms, you need Xalan, which will internally use SAP's parsers I guess, but its own transformer. Include the xalan.jar in your lib folder of whatever type of application you are trying to make.

public Result validate(Source xmlsrc) 
    throws TransformerException,
           TransformerConfigurationException,  
           ParserConfigurationException
  {
  	
  	//All this to ensure thread safety
  	ClassLoader oldLoader=null;
	
	try {
	   oldLoader = Thread.currentThread().getContextClassLoader();
	   Thread.currentThread().setContextClassLoader(
	   this.getClass().getClassLoader());

	/*
	 * This is the important part. The TransformerFactoryImpl is
	 * org.apache.xalan.processor.TransformerFactoryImpl.
	 * Create and instance of the factory directly using the
	 * constructor (new TransformerFactoryImpl()).
	 */
	   Transformer validator =
		 new TransformerFactoryImpl().newTransformer(new DOMSource(domValidator.getNode()));
		 
		 
	   DOMResult result = new DOMResult();	   
	   
	   validator.transform(xmlsrc, result);

	   return new Result(result, getWarnings());	   
	} finally {
	   Thread.currentThread().setContextClassLoader(oldLoader);
	}
  }

Cheers!

Nirav

Answers (2)

Answers (2)

Former Member
0 Kudos

Nirav,

Seems that in stand-alone Java and in PAR different XSLT transformers are used. Try to output trfactory.getClass().getName()

May be you need to alter PAR references to include other XSLT tranfromation library (say, from sapxmltoolkit)

Valery Silaev

EPAM Systems

http://www.NetWeaverTeam.com

Former Member
0 Kudos

Hi,

I have still not managed to solve the issue. What I have done now is dumped the output of the first transform and written a separate PAR just to try the seciond transform. But this fails too. There doesn;t seem to be anything wrong with the XSL generated in the first step since the second step works fine using jaxp on my local machine and testing with IE. Has anyone faced issues with transforming in SAP? I am using SP16.

Nirav

Former Member
0 Kudos

This is the result from the first transform and I need to use this for the second transform. Am I doing something wrong?

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
	<xsl:template match="/">
	 <RESULT>
	  <TITLE>XSL for validating a Sales Order</TITLE> 
	  <xsl:apply-templates mode="M1" select="/" /> 
	  </RESULT>
	</xsl:template>
	<xsl:template mode="M1" priority="4" match="salesOrder">
	...
	</xsl:template>
	<xsl:template mode="M1" priority="3" match="billTo">
	...
	</xsl:template>
	<xsl:template mode="M1" priority="2" match="shipTo">
	...
	</xsl:template>
	<xsl:template mode="M1" priority="1" match="salesItem">
	...
	</xsl:template>
</xsl:stylesheet>

I get the result of the first template (<xsl:template match="/">) only.

Former Member
0 Kudos

Hi Nirav,

just want to confirm that you're not alone with the issue. I also experience problems with XSL transformations (see my messages in ) - for me after server restart the SAME transformation scenario works 5-6 times and then stops working. My problems were related to attributes - they are not properly generated in the XSL transformation pipe.

I'm pretty sure that there are some bugs in the xmltoolkit(which, according to the timestamps is very old implementation - I'm on SP12) likely in cache instantiation, but I found no way how to alter XSLT processor for my WD applications (xmltoolkit is one of the base libraries of WebAS and I failed with attempts to find a way to replace the processor).

Best regards,

Nick

Former Member
0 Kudos

Hi Valery / Nick,

trfactory.toString() gives com.sap.engine.lib.jaxp.TransformerFactoryImpl@730f696f, which means it is using SAP's libraries. I think these have bugs. Now i'm trying to use Xalan and Xerces. I added the xerces.jar and xalan.jar, but still can't get the reference to change. This is my code now:

		ClassLoader oldLoader = null;
		TransformerFactory trsfactory = null;
		String transName = null;

		try {
			oldLoader = Thread.currentThread().getContextClassLoader();
			Thread.currentThread().setContextClassLoader(
				this.getClass().getClassLoader());
			trsfactory = TransformerFactory.newInstance();
			
			transName = trsfactory.toString();
			
			Templates template =
				trsfactory.newTemplates(
					new StreamSource(new FileInputStream(schemaPath)));
			DOMResult result = new DOMResult();
			Transformer validator = template.newTransformer();
			validator.transform(
				new StreamSource(new FileInputStream(filePath)),
				result);
			return  getDOMString(result)
					+ "<br>" + transName;
		} finally {
			Thread.currentThread().setContextClassLoader(oldLoader);
		}

Like Nick, I dont want to change the properties at the server.

Former Member
0 Kudos

Nirav,

This is option of last resort, but anyway...

Instead of *Factory classes / methods you can create DOMDocument, Template & Co using explicit constructors of classes exposed by Xerces/Xalan.

Try this if nothing else helps.

SAP XmlToolkit seems to be the default XML/XSLT implementations discovered via Java Services API.

Valery Silaev

EPAM Systems

http://www.NetWeaverTeam.com

Former Member
0 Kudos

:'( ... thats sad! Do you think it makes sense reporting this issue in an OSS message. I think there could be a bug in SAP's APIs which should be corrected. What say Valery?

Ok ... as the last resort, I try to access Xalan's TransformerFactoryImpl to return the TransformFactory. But this too returns com.sap.engine.lib.jaxp.TransformerFactoryImpl. I think this may be because Xerces's TransformerFactoryImpl extends javax.xml.transform.sax.SAXTransformerFactory which again returns SAP's API. Is there a way out?

Former Member
0 Kudos

Done calling Xalan's constructor directly. Thanks Valery. But still...dont you think SAP should correct this?

Former Member
0 Kudos

Hi Nirav,

could you please post piece of your code + configuration description here and close the thread - thus people, who look for the solution will find it here?

Thanks!

Nick