Hi,
I am currently using Axis 1.1. There is nothing stopping me moving to Axis 1.2-beta other than the fact that I have no information on how stable the beta release is. Do you (or anybody else) have any opinions on the relative stability of Axis 1.1 versus Axis 1.2.
The XML string which I receive is generated by a mainframe application which does a conversion between an old proprietary data format and XML.
In your code you mention that it is not necessary to register Axis classes explicitly in Axis 1.2 - that Axis 1.2 can do this dynamically. In that case I probably need to use Axis 1.2 as this is functionality that I would require. I cannot use a hardcoded list of data classes (as done in the sample code provided) as I have no control over the contents of the WSDL file (which is defined by an external partner) so its possible that the required datatypes can (and do) change regularly. Of course it would be possible to use a hardcoded array of the type classes if we were to use the WSDL file along with an XSL file to generate our serializsation/deserialization source code but I would prefer not to have to do this. If Axis 1.2 can handle the datatype registration dynamically then this would be preferable. Do you know how this dynamic registration is provided in Axis 1.2? Axis does not seem to provide any release notes detailing functionality changes from one release to the next?
Michael thanks very much for the sample code as it is exactly what I was looking for :)
Regards, Tom
From: "Michael Binz" <[EMAIL PROTECTED]> To: [EMAIL PROTECTED] CC: [EMAIL PROTECTED] Subject: RE: Deserializing XML w/o using client stubs Date: Tue, 28 Sep 2004 10:45:08 +0200 (MEST)
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 * [EMAIL PROTECTED] pContext MessageContext where all the classes will be registered [EMAIL PROTECTED] 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:[EMAIL PROTECTED] > Sent: Monday, September 27, 2004 10:44 AM > To: [EMAIL PROTECTED] > 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 +++
_________________________________________________________________
Get ready for school! Find articles, homework help and more in the Back to School Guide! http://special.msn.com/network/04backtoschool.armx