开发者

JAXB How to force xsi:type in array of subclasses? (JBoss 4.2.3)

开发者 https://www.devze.com 2023-03-18 03:53 出处:网络
(Please note; i\'m completely new to webservices, so the following may be stupid/incorrect but please be patient)

(Please note; i'm completely new to webservices, so the following may be stupid/incorrect but please be patient)

In my WebServices' @WebMethod I'm returning an array of an abstract base class (JAXB annotated entities in JBoss 4.2.3).

Obviously unless the type information is encoded per array element this will fail...

So how do I ensure that JAXB adds the xsi:type attribute?

My WebService interface has the following annotation, and I've tried every legal combination:

@SOAPBinding(style = RPC, parameterStyle = WRAPPED, use = LITERAL)

the methods on this interface take x2 parameters annotated @WebParam(name="...", mode=IN)

Other methods with similar signatures that don't return a heterogeneous array work perfectly.


Some related things:

Looks like JBoss uses the types defined in the method signatures to decide what classes to load into the JAXBContext - if I change the return types to Object[] it throws an error stating that the AbstractBase class "nor any of its super class is known to this context." I've added dummy methods returning the specific subclasses so that the generated WSDL has a list of all of them.

when I try to write tests for this, all is ok for single elements, but JAXB throws an error for array types: unable to marshal type "[LAbstractBase;" as an element because it is missing an @XmlRootElement annotation

From code like that shown below (note: AbstractBase, ConcreteOne and ConcreteTwo all have @XmlRootElement annotations)

private static final Class<?>[] CLASSES_TO_BE_BOUND = new Class<?>[]{
    //Note; adding AbstractBase[].class doesn't work either
    AbstractBase.class, ConcreteOne.class, ConcreteTwo.class
};

@Test
public void testXsiTypeAttributeIsIncludedInHeterogeneousArray()
{
    AbstractBase[] array = new AbstractBase[2];
    array[0] = new ConcreteOne();
    array[1] = new ConcreteTwo();
    Marshaller marshaller = createMarshaller();
    StringWriter sw = new StringWriter();
    marshaller.marshal(array, sw);
    String output = sw.toString();
    Assert.assertTrue(output.contains("xsi:type=\""));
}


private Marshaller createMarshaller() throws Exception {
    JAXBContext context = JAXBContext.newInstance(CLASSES_TO_BE_BOUND);
    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    return marshaller;
}

Ideally I'd like to be able to test some bits relating to arrays, but it's far more critical that I can get the type information encoded per element in the JBoss environment.


Edit: Own Answer

JBoss (4.2.3) is doing something clever, but not too clever - it will handle the returning of arrays but not polymorphic ar开发者_如何转开发rays. This threw me a bit as I tried to get this way of doing it working in my tests.

Instead of trying to solve the JBoss WebService issue I made my tests more comprehensive - making the array a member of a simple container class and then annotating the array's getter with:

@XmlElementRefs({
    @XmlElementRef(type = ConcreteOne.class),
    @XmlElementRef(type = ConcreteTwo.class)
})
public AbstractBase[] getItems() { /*...*/ }

Which worked, and returning this in the JBoss WebService also worked! Though I'm using:

@SOAPBinding(style = DOCUMENT, parameterStyle = BARE, use = LITERAL)

So it's not adding the xsi:type attribute, but document nodes are correctly tagged:

<ConcreteOne>...</ConcreteOne>

At some point I'll change the WebService to use RPC as I don't really like the single argument restriction, but for now this is working fine.

0

精彩评论

暂无评论...
验证码 换一张
取 消