You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-user@axis.apache.org by Michael Binz <mi...@gmx.de> on 2004/09/28 10:45:08 UTC

RE: Deserializing XML w/o using client stubs

Tom,

you're not saying for which version of Axis you need a solution, and how the
XML string that you receive is generated.

The code below is a complete example that we use to serialise and
deserialise Axis-generated classes.

We use this code to write WebService requests to files that can be later-on
used to perform regression testing, i.e. the files are deserialised into a
request structure that is then sent to the server.

The code is for Axis 1.1, the main problem with Axis 1.1 is that we have to
manually create a TypeMapping holding references to the generated classes,
with Axis 1.2 this would not be necessary.

The interesting entry points in the code are xmlToAxis() which transforms
from an XML file to an  instance of the passed class and axisToXml() which
transforms from an instance of an Axis stub to the XML string.  This is
symmetric, the string that comes out of axisToXml() can be fed again into
xmlToAxis() to get the equivalent object structure.

Most bits and pieces in the code are collected from this mailing list.

Hope that helps,
Michael.



=== START ===
package .util;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.ByteArrayInputStream;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.Properties;
import java.util.StringTokenizer;

import javax.xml.namespace.QName;
import javax.xml.rpc.encoding.TypeMapping;

import net.guardean.agw.*;
import net.guardean.agw.server.AgTransformer;
import net.guardean.agw.ws.schema.*;

import org.apache.axis.MessageContext;
import org.apache.axis.client.AxisClient;
import org.apache.axis.client.Call;
import org.apache.axis.description.TypeDesc;
import org.apache.axis.encoding.DeserializationContextImpl;
import org.apache.axis.encoding.SerializationContextImpl;
import org.apache.axis.message.RPCElement;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.axis.message.SOAPBodyElement;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.AttributesImpl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.axis.encoding.ser.BaseDeserializerFactory;
import org.apache.axis.encoding.ser.BaseSerializerFactory;
import org.apache.axis.encoding.ser.BeanSerializerFactory;
import org.apache.axis.encoding.ser.BeanDeserializerFactory;
import org.apache.axis.encoding.ser.EnumDeserializerFactory;
import org.apache.axis.encoding.ser.EnumSerializerFactory;



/**
 * Holds AG support functionality.
 *
 * @version $Revision: #4 $
 * @author Michael Binz
 */
public class AgSupportLib
{
    /**
     * Logger used by this class
     */
    private static final Log _logger =
        LogFactory.getLog( AgSupportLib.class );



	/**
	 * Generated classes from Axis.  This table is only necessary on
Axis 1.1.
     * Note that the classes commented with // enum ... require a special
     * handling performed in registerAxisClassesInContext.  Keep them as
     * as comments here so that they arent forgotten.
     *
     * TODO Remove as soon as Axis 1.2 is used.
	 */
	private static Class generatedAxisClasses[]={
		AddressType.class,
		AgencyInformationType.class,
		CompanyType.class,
		ErrorMessageType.class,
		FunctionType.class,
// enum GenderType.class,
		LegalEventType.class,
		MoneyType.class,
		OptionalEntry.class,
		PartyListType.class,
		PartyMatrixType.class,
		PartyType.class,
		PersonType.class,
		RatingType.class,
		RequestType.class,
		ResponseType.class,
		ScoreType.class
	};

    private final static QName GenderTypeName = 
        new javax.xml.namespace.QName(
            "http://ws.agw.guardean.net/schema",
            getPlainClassName( GenderType.class ) );




	/**
	 * Register a set of generated classes from Axis in a Message
Context
	 *
	 *@param pContext MessageContext where all the classes will be
registered
	 *@param pClassArray generated classes by Axis that will be register
in the 
     *       context given as parameter
	 */
	private static void registerAxisClassesInContext(MessageContext
pContext, Class pClassArray[]){
		Class currentClass = null;
		//TODO Is there a way to check dynamically the classes
generated by Axis 
		//under the schema package??
        //TODO Operation is not longer necessary on Axis 1.2.  No need to
invest
        // the time to make that finally cool on Axis 1.1 -- would be
expensive.
		if (pContext == null)
			return;
			
		if (pClassArray == null){
			_logger.warn("No generated classes from Axis in
system!.");
			return;
		}

		TypeMapping typeMapping = pContext.getTypeMapping();

		for(int i=0; i < pClassArray.length; i++){
		    try{
		        currentClass = pClassArray[i];
		        // get TypeDesc from class
		        Method gtd = currentClass.getMethod( "getTypeDesc",
null );
		        TypeDesc td = (TypeDesc)gtd.invoke( null, null );
		        // register serializer and deserializer for this
class				
		        typeMapping.register( 
                        currentClass,
		                td.getXmlType(), 
	
BaseSerializerFactory.createFactory(BeanSerializerFactory.class,currentClass
,td.getXmlType()),
 
BaseDeserializerFactory.createFactory(BeanDeserializerFactory.class,currentC
lass,td.getXmlType())
            );
			}catch(Exception e){
				_logger.error("No Axis generated class ("+
currentClass.getName()+")",e);
            	throw new IllegalArgumentException(
                	"No Axis generated class: " + currentClass.getName()
);		
			}
		}

		// Special handling for enumeration simple types.
        // genderType
        {
            typeMapping.register( 
                GenderType.class,
                GenderTypeName, 
                EnumSerializerFactory.createFactory(
                        EnumSerializerFactory.class,
                        GenderType.class,
                        GenderTypeName ),
                EnumDeserializerFactory.createFactory(
                        EnumDeserializerFactory.class,
                        GenderType.class,
                        GenderTypeName ) );
        }
	}



    /**
     * <p>Transforms an instance of a type generated by Axis
     * <code>WSDL2Java</code> into an XML representation, using only Axis
APIs.
     * <p>Note that this implementation only works on Axis data types since
     * only these are registered against the Axis runtime,</p>
     *
     * @param pO The object to be transformed.
     * @param pQname The qualified name of the passed object.  Note that
     *        passing the wrong value here does not result in an error, but
in
     *        the generation of a wrong XML structure.  This can be accessed

     *        for a given type <i>Type</i> by the code 
     *        <code><i>Type</i>.getTypeDesc().getXmlType()</code>.
     * @return An XML-stringified representation of the input object.
     * @deprecated Use the single parameter version of this operation.
QName
     *             autodetection is save and simple.
     */
    private static String transformWsInstanceToXml( Object pO, QName pQname
)
    {
        // Since we are not in a message call context, we have to create a
        // message context ourselves.
        MessageContext mc = new MessageContext( new AxisClient() );
        registerAxisClassesInContext( mc, generatedAxisClasses );
        // This means that the XSI type attributes are not generated.
        mc.setProperty( Call.SEND_TYPE_ATTR, "false" );

        // This writer will ultimately receive the generated XML...
        StringWriter result = new StringWriter();
        // ...and is connected to the serialisation context.
        SerializationContextImpl sci = new SerializationContextImpl(
            result,
            mc );
        // Pretty print the result for convenience.
        sci.setPretty(  true );

        // MultiRef'ing means that when object graphs are serialized, the
        // identity of objects is kept, i.e. first the first level objects
are
        // serialized with hrefs to the nested objects.  After that the
nested
        // objects are serialised and so on.
        // This is not what we want.  We want instead an in-place expansion
of
        // nested objects without the hassle of object decomposition, we
have
        // only plain vanilla data structures in the end.
        sci.setDoMultiRefs( false );

        try
        {
            // ...and perform the serialization.
            sci.serialize(
                    pQname,
                    new AttributesImpl(),
                    pO );
        }
        catch ( IOException e )
        {
            // This exception is triggered by the java.io.Writer in the
            // SerialisationContext.  Since we use a StringWriter, we can
            // guarantee that no IOExceptions will be thrown and safely
            // ignore these.
            _logger.warn( "java.io.StringWriter threw IOException.", e );
        }

        return result.toString();
    }



    /**
     * Transforms the passed type into its XML representation.  The passed
     * object must represent a type generated by the Axis WSDL compiler.
     *
     * @param pO The object to be transformed.
     * @return The passed object's XML representation in string format.
     * @throws IllegalArgumentException If the passed object is not an
instance
     *         of an Axis-generated type.
     */
    public static String axisToXml( Object pO )
    {
        Class oClass = pO.getClass();

        try
        {
            // Get the type description via the static method.
            Method gtd = oClass.getMethod( "getTypeDesc", null );
            TypeDesc td = (TypeDesc)gtd.invoke( null, null );
            // From the type description get the QName.
            return transformWsInstanceToXml( pO, td.getXmlType() );
        }
        catch ( Exception e )
        {
            // Check whether this is one of the non-complex types in the
            // package.  In this case we synthesize a QName. Holy cow,
looking
            // forward to Axis 1.2
            return transformWsInstanceToXml(
                    pO,
                    new javax.xml.namespace.QName( 
                            "http://ws.agw.guardean.net/schema",  
                            getPlainClassName( pO.getClass() ) ) );  
        }
    }



    /**
     * Transforms the passed type into its XML representation.  The passed
     * object must represent a type generated by the Axis WSDL compiler.
     *
     * @param pO The object to be transformed.
     * @return The passed object's XML representation in string format.
     * @deprecated Use axisToXml() instead.
     * @throws IllegalArgumentException If the passed object is not an
instance
     *         of an Axis-generated type.
     */
    public static String transformWsInstanceToXml( Object pO )
    {
        return axisToXml( pO );
    }



    /**
     * Convert an XML structure created from an Axis-generated class back
into
     * an object representation.
	 * 
     * @param xml The XML structure.  Generated by
     *        <code>axisToXml( Object )</code>.
     * @param pQname The qualified name of the target type.
     * @return An instance of the Axis generated class.
     * @see AgSupportLib#axisToXml(Object)
     */
    public static Object xmlToAxis( String xml, QName pQname )
    {
        // TODO would be cool to dynamically detect the target qname.  This
        // could be done by looking at the first element's name in the
inbound
        // XML data.

        try
        {
    		// Wrap the InputStream up into a SOAP Body and Envelope
since the parser
		    // expects a SOAP Envelope
    		SOAPEnvelope env1 = new SOAPEnvelope();
    		env1.addBodyElement(
                    new SOAPBodyElement(
                            new ByteArrayInputStream(xml.getBytes())));
        	
            // Since we are not in a message call context, we have to create
a
            // message context ourselves.
            MessageContext mc = new MessageContext( new AxisClient() );
            // TODO: This following line manually registers the generated
type
            // mappings.  This has to be removed as soon as Axis 1.2 is
used.
	
registerAxisClassesInContext(mc,generatedAxisClasses);
			
            InputSource is = 
                new InputSource( new StringReader( env1.toString() ) );

            // ...and is connected to the serialisation context.
            DeserializationContextImpl sci = new DeserializationContextImpl(
                    is, mc, org.apache.axis.Message.REQUEST );
            sci.parse();

            SOAPEnvelope env2 = sci.getEnvelope();
            RPCElement rpcElem = (RPCElement) env2.getFirstBody();
             
            // On Axis 1.2 use the getValueAsType( qname, class ) in the
next
            // line and not the single arg version.
            //return rpcElem.getValueAsType(qname, clazz);
            return rpcElem.getValueAsType( pQname ); // Axis 1.1 code.
        }
        catch ( Exception e )
        {
            _logger.debug( "deserialisation problem.", e );
            throw new IllegalArgumentException(
              e.getMessage() );
        }
    }



    /**
     * 
     * @param pclass
     * @return
     */
    public static String getPlainClassName( Class pclass )
    {
        String fullyQualified = pclass.getName();
        int lastDot = fullyQualified.lastIndexOf( '.' );
        if ( lastDot == -1 )
            return fullyQualified;
        
        return fullyQualified.substring( lastDot+1 );
    }



    /**
     * Test code.
     *
     * @param argv
     */
    public static void main( String[] argv )
    {
        LegalEventType o = new LegalEventType();

        o.setDate( new Date() );
        o.setDescription( "This is a test for legal event serialisation." );
        o.setHasExpired( Boolean.TRUE );
		
		System.out.println( "Serialized Object. Description: " +
o.getDescription() );
		System.out.println( "Serialized Object. hasExpired: " +
o.getHasExpired() );
		System.out.println( "Serialized Object: Date: " +
o.getDate() );
		
        String intermediateString = transformWsInstanceToXml( o );

        Object deserialised = xmlToAxis(
            intermediateString,
            LegalEventType.getTypeDesc().getXmlType() );

		LegalEventType t = (LegalEventType) deserialised;

		System.out.println( "Deserialized Object. Description: " +
o.getDescription() );
		System.out.println( "Deserialized Object. hasExpired: " +
o.getHasExpired() );
		System.out.println( "Deserialized Object: Date: " +
o.getDate() );
        //System.err.println( "" + deserialised );
        
        GenderType gender = GenderType.FEMALE;
        intermediateString = transformWsInstanceToXml( gender );
        System.err.println( intermediateString );
        deserialised = xmlToAxis( intermediateString, GenderTypeName );
        System.err.println( deserialised.toString() );
        Gender g = AgTransformer.transform( (GenderType)deserialised );
        System.err.println( "Gender is: " + g );
    }
}
=== END ===


> -----Original Message-----
> From: tom ONeill [mailto:beagleboythefirst@hotmail.com]
> Sent: Monday, September 27, 2004 10:44 AM
> To: axis-user@ws.apache.org
> Subject: Deserializing XML w/o using client stubs 
> 
> 
> Hi all,
> 
> I have used the WSDL2Java utility to generate client stubs to 
> invoke an 
> external partners web service. My component receives a large 
> XML string 
> which represents the datatype which will be passed as a 
> parameter to the web 
> service call. Thus I need a way of deserializing this XML 
> string into the 
> appropriate Java type (as oppossed to creating the Java type 
> within my code 
> and populating its member variables). I would expect to be 
> able create some 
> kind of Document object from my XML string and then use an Axis 
> derserializer to return the appropriate Java type. However if 
> is not clear 
> from the Axis API how I might do this.
> 
> Any help appreciated.
> 
> Regards,
> Tom
> 
> _________________________________________________________________
> MSN 8 helps eliminate e-mail viruses. Get 2 months FREE*. 
> http://join.msn.com/?page=features/virus
> 

-- 
GMX ProMail mit bestem Virenschutz http://www.gmx.net/de/go/mail
+++ Empfehlung der Redaktion +++ Internet Professionell 10/04 +++