11

I want to marshal object to XML.

However, it fails with exception:

javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.SAXException2: unable to marshal type "FreightOfferDetail" as an element because it is missing an @XmlRootElement annotation]
    at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:331)
    at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:257)
    at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:96)
    at com.wktransportservices.fx.test.util.jaxb.xmltransformer.ObjectTransformer.toXML(ObjectTransformer.java:27)
    at com.wktransportservices.fx.test.sampler.webservice.connect.FreightOfferToConnectFreight.runTest(FreightOfferToConnectFreight.java:59)
    at org.apache.jmeter.protocol.java.sampler.JavaSampler.sample(JavaSampler.java:191)
    at org.apache.jmeter.threads.JMeterThread.process_sampler(JMeterThread.java:429)
    at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:257)
    at java.lang.Thread.run(Thread.java:662)
Caused by: com.sun.istack.SAXException2: unable to marshal type "FreightOfferDetail" as an element because it is missing an @XmlRootElement annotation
    at com.sun.xml.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:244)
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:303)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:490)
    at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:328)

In fact, this annotation is present (for parent and delivered class):

@XmlRootElement(name = "Freight_Offer")
@XmlAccessorType(XmlAccessType.FIELD)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class FreightOffer {
    @JsonIgnore
    @XmlTransient
    private String freightId;
    private String id;

    private String externalSystemId;

    private AddressLocation pickUp;

    private AddressLocation delivery;

    private FreightDescription freightDescription;

    private ListContacts contacts;

    private Customer customer;

    private ListSla slas;

    private String pushId;

    private CompanyProfile company;

    private Route route;

    private String href;

    private Lifecycle lifecycle;

    private Visibility visibility;

    private Boolean unfoldedVXMatching;
    // getters / setters

Child class:

@XmlAccessorType(XmlAccessType.PROPERTY)
public class FreightOfferDetail extends FreightOffer {

    private List<Contact> contact;

    @XmlElement(name = "contacts")
    @JsonProperty("contacts")
    public List<Contact> getContact() {
        return contact;
    }

    public void setContact(List<Contact> contact) {
        this.contact = contact;
    }

It fails exactly at this method toXML():

public class ObjectTransformer<T> implements Transformer<T> {

    protected final JAXBContext context;
    protected final Marshaller marshaller;

    protected final int okStatusCode = 200;
    protected final String okSubErrorCode = "OK";

    public ObjectTransformer(JAXBContext context) throws JAXBException {
        this.context = context;
        marshaller = context.createMarshaller();
        marshaller.setProperty("jaxb.encoding", "UTF-8");
        marshaller.setProperty("jaxb.formatted.output", Boolean.TRUE);
    }

    public String toXML(T object) throws JAXBException {
        StringWriter writer = new StringWriter();
        marshaller.marshal(object, writer);
        String xmlOffer = writer.toString();
        return xmlOffer;
    }

It should work, but it shouldn't.

I couldn't find what is missed or wrong here.

UPDATE:

Here is snippet from test:

public SampleResult runTest(JavaSamplerContext context) {
    AbstractSamplerResults results = new XMLSamplerResults(new SampleResult());
    results.startAndPauseSampler();

    if (failureCause != null) {
        results.setExceptionFailure("FAILED TO INSTANTIATE connectTransformer", failureCause);
    } else {
        FreightOfferDTO offer = null;
        FreightOffer freightOffer = null;
        try {
            results.resumeSampler();            

            RouteInfo routeDTO = SamplerUtils.getRandomRouteFromRepo(context.getIntParameter(ROUTES_TOUSE_KEY));

            offer = FreightProvider.createRandomFreight(routeDTO, createUserWithLoginOnly(context));

            freightOffer = connectTransformer.fromDTO(offer);
            String xmlOfferString = connectTransformer.toXML(freightOffer); // <- it fails here.

I take the date from CSV file and converting to DTO object. This method returns to me FreightOfferDetail.

Here is snippet from this method:

public FreightOfferDetail freightFromDTO(FreightOfferDTO freightDTO, boolean fullFormat){
    FreightOfferDetail freight = new FreightOfferDetail();

    freight.setFreightId(freightDTO.getIds().getAtosId());
    freight.setId(freightDTO.getIds().getFxId());
    // ...

How to marshal object to XML file, at this case?

catch23
  • 13,661
  • 38
  • 120
  • 194

4 Answers4

14
public String toXML(T object) throws JAXBException {
  StringWriter stringWriter = new StringWriter();

  JAXBContext jaxbContext = JAXBContext.newInstance(T.class);
  Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

  // format the XML output
  jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

  QName qName = new QName("com.yourModel.t", "object");
  JAXBElement<T> root = new JAXBElement<Bbb>(qName, T.class, object);

  jaxbMarshaller.marshal(root, stringWriter);

  String result = stringWriter.toString();
  LOGGER.info(result);
  return result;
}

Here is the article I use when I have to marshal/unmarshal without @XmlRootElement: http://www.source4code.info/2013/07/jaxb-marshal-unmarshal-with-missing.html

All the best hope it helps :)

Lazar Lazarov
  • 2,112
  • 2
  • 23
  • 32
4

Note that the error message talks about FreightOfferDetail, not FreightOffer.

Based on that, I would expect that somewhere (outside the provided code), you're asking a Detail to be marshalled.

If you want to be able to do that (with JAXB?) you need to annotate that class as well with the XmlRootElement annotation.

catch23
  • 13,661
  • 38
  • 120
  • 194
  • I tried your suggestion. Result is the same - `javax.xml.bind.MarshalException - with linked exception: [com.sun.istack.SAXException2 ...` – catch23 May 05 '16 at 14:45
  • Could have you include the entire stack trace? The exception type doesn't say a lot without the accompanying exception message. And the actual marshalling call would help to narrow it down (i.e. what type of object is actually being marshalled?). – Alain Van Hout May 05 '16 at 15:25
  • View the question. It contains exactly this exception. – catch23 May 05 '16 at 15:27
  • Okay, I'm just asking because in the original question you seemed to not have noticed that the exception did not refer to the class that you pointed out as indeed having that annotation. – Alain Van Hout May 05 '16 at 15:30
  • that snippet doesn't include an actual toXml() call, and the Detail class contains List field. Is that field annotated with the needed XML annotations? And based on the initial feedback, could you update the class definition annotations? – Alain Van Hout May 05 '16 at 15:38
  • Contact has only `XmlAccessorType`. About which class definition have you exactly talked? – catch23 May 05 '16 at 15:51
  • I'm talking about FreightOfferDetail (rather than FreightOffer) since that's what's mentioned in the original stacktrace message. To reiterate: the exception indicates that you are converting a Detail directly and that it therefore needs that one annotation. – Alain Van Hout May 05 '16 at 16:04
1

You can use the ObjectFactory class to workaround for the classes which doesn't have the @XmlRootElement. ObjectFactory has overloaded methods.

Method:1 It does simple creation of the object and

Method:2 It will wrap the object with @JAXBElement.

Always to use Method:2 to avoid javax.xml.bind.MarshalException - with linked exception missing an @XmlRootElement annotation

Method:1

public GetCountry createGetCountry() {
        return new GetCountry();
    }

Method:2

 @XmlElementDecl(namespace = "my/name/space", name = "getCountry")
 public JAXBElement<GetCountry> createGetCountry(GetCountry value) {
        return new JAXBElement<GetCountry>(_GetCountry_QNAME, GetCountry.class, null, value);
    }

for more information follow this stackoverflow-question

Hope this will be helpful...

prasadg
  • 539
  • 6
  • 10
0

When creating the Java-Objects from the WSDL with the apache-cxf maven-plugin, using sth. like:

<plugin>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-codegen-plugin</artifactId>
    ...
</plugin> 

There will be a automatically generated ObjectFactory.

When configuring your Exception in Java you can use this generated ObjectFactory for returning the fault. The important step is to return a JAXBElement<YourSoapFault> in the getFaultInfo() method of your exception.

@WebFault(name = "YourSoapFault",
        targetNamespace = "ns://somenamespace")
public class SomeWebServiceException extends RuntimeException {
    private final YourSoapFault fault;

    public SomeWebServiceException(String message, YourSoapFault fault) {
        super(message);
        this.fault = fault;
    }

    public SomeWebServiceException(String message, YourSoapFault fault, Throwable e) {
        super(message, e);
        this.fault = fault;
    }

    public JAXBElement<YourSoapFault> getFaultInfo() {
        return new ObjectFactory().createYourSoapFault(fault);
    }

}
duffy356
  • 3,468
  • 3
  • 29
  • 43
  • On that project we had to use ant as a build tool. And my main goal is marshal object to XML instead of wrapping fault message. I have exception stack trace from a console. – catch23 Jun 22 '18 at 14:53