javaxmljaxbmarshallingmoxy

How to add wrapper element to class during the JAXB marshalling


I am trying to create XML using the Moxy Marshalling approach. Everything seems to be working fine except for one small thing. Basically, I would like to know how to add wrapper element to the class itself during the marshalling.

As we are aware we can add @XmlPath("test/name/text()") to add a wrapper to any of the String elements. Also for collection we can make use of @XmlElementWrapper(name="languages"), @XmlElement(name="language"). But these are for the fields within the class. How can I add the wrapper element to the class itself which is being marshalled?

I have the following class:

@XmlRootElement(name = "customer")
@XmlType(name="customer",propOrder={"name","languages"})
@XmlAccessorType(XmlAccessType.FIELD)   
public class Customer{
    
    @XmlPath("extension/name/text()")
    private String name;
    
    @XmlElementWrapper(name = "languages")
    @XmlElement(name = "language")
    private List<String> languages;
    
    //Getter, Setter and other constuctors
}

This will produce the output XML something like this:

<customer>
    <extension>
        <name>Hello</name>
    </extension>
    <languages>
        <language>English</language>
        <language>UK English</language>
    </languages>
</customer>

However, I would like to add the wrapper element to event the whole class so that output would look something like this: (Notice the whole customer is wrapper within classWrapper)

<classWrapper>
    <customer>
        <extension>
            <name>Hello</name>
        </extension>
        <languages>
            <language>English</language>
            <language>UK English</language>
        </languages>
    </customer>
</classWrapper>

I tried adding the @XmlPath and @XmlElementWrapper to the Customer class but it throws an error as it can be added only to the fields within the class but not to the whole class.

Following is my Main class which would be used for marshalling:

public class HelloWorld{

     public static void main(String []args){
        Customer customer = new Customer();
        
        List<String> languages = new ArrayList<String>();
        languages.add("English");
        languages.add("UK English");
        
        customer.setName("Hello");
        customer.setLanguages(languages);
        
        JAXBContext context = JAXBContext.newInstance(Customer.class);
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal(customer,System.out);
     }
}

I am aware that I can write another wrapper class add the custom field to it then use that wrapper class for the marshalling. Actually, I am doing the same as of now. However, trying to find if there is some Moxy approach of doing this similar to how we have @XmlPath and @XmlElementWrapper

Can someone please suggest some form of Moxy Jaxb related approach for achieving the wrapper element to the whole class?


Solution

  • This is a temporary workaround that works as expected for me. Posting the answer here so it can be helpful to someone in the future.

    1. Change the @XmlRootElement("customer") to @XmlRootElement("classWrapper"). So you will get the classWrapper as the outer element.

    2. Then change all the element within the Customer class using the @XmlPath so that all element go under the Customer tag. So overall customer.class would look something like this:

        @XmlRootElement(name = "classWrapper")
        @XmlType(name="customer",propOrder={"name","languages"})
        @XmlAccessorType(XmlAccessType.FIELD)
        @Getter
        @Setter
        public class Customer{
        
          @XmlPath("customer/extension/name/text()")
          private String name;
        
          @XmlPath("customer/languages/language/text()")
          private List<String> languages;
        
          //Getter, Setter and other constuctors
        }
    

    Just a tip while using the @XmlPath: do not use text() if its not simple type such as String,Date, etc type.

    For example if the element is of Custom type with List then do not use /text()

    @XmlPath("extension/elements/element")
    List<myType> elements;
    

    This will add extension -> elements -> element then content.

    <extension>
    <elements>
    <element></element>
    <element></element>
    ......
    </elements>
    </extension>
    

    If the elements are of String type then you have to use text()

    @XmlPath("extension/elements/text()")
    List<String> elements;