2

I am trying to create the XML using the marshaling method from the JAXB library. For this approach, I am using the standard java class and assigning the annotation to it. I would like to have a wrapper element to some of the element in the class which are not Collections.

I am ware of @XmlElementWrapper which can be used for Collections but I do not have a collection. I have some elements for which I want to have an outer XML element so that it can match the standard XSD.

I just want to know if there is a way to add an outer XML element to some of the elements so that the create XML matches the standard XSD format. If there is no direct approach then what alternative approach can I take?

As we can see from the example XML the carinfo tag is a outer (wrapper) tag for the elements engine and year but this carInfo tag is not present within the Parent or Child class. As these are standard classes I cannot modify the fields/ Add new fields. Hence I would like to handle wrapper XML tag addition using the JAXB.

Following is the input JSON:

{
   "brand": "Ferari",
   "build": "Italy",
    "engine": "Mercedes",
    "year": "2021"
   
}

Following is the output XML that I would like to have:

<?xml version="1.0"?>
<Car>
    <brand>Ferari</brand>
    <build>Italy</build>
    <carinfo>
        <engine>Mercedes</engine>
        <year>2021</year>
    </carinfo>
</Car>

Following are my classes:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlTransient
@XmlType(name = "Parent")
public class Parent{
    private String brand;
    private String year;
    
    //Getters and Setters avoided
}

Class based on which XML is being created:

@XmlRootElement(name = "Car")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Child",propOrder={"brand","engine","build","year"})
public class Child extends Parent{
    private String engine;
    private String build;
    
    //Getter and Setters avoided
}

The main class which will create the XML: I am using the Jackson library to read the values from the Json file and create the XML based on Jaxb annotations.

public class Main{

     public static void main(String []args){
        JsonFactory jsonFactory = new JsonFactory();
        JsonParser jsonParser = jsonFactory.createParser(new File(Main.class.getClassLoader().getResource("input.json").toURI()));
        Child eventInfo = jsonParser.readValueAs(Child.class);
        JAXBContext context = JAXBContext.newInstance(Child.class);
        Marshaller mar = context.createMarshaller();
        mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        mar.marshal(eventInfo, System.out);
     }
     
     
}
BATMAN_2008
  • 2,788
  • 3
  • 31
  • 98
  • Hello, could you add the input.json ? – dariosicily Apr 27 '21 at 16:10
  • @dariosicily Sorry for missing out on adding the `Json`. I have added now. Please check. As you can see from JSON I do not have `carInfo` tag but in the `XML` I would like to add `carInfo` tag. Is there a way I can add the additional tag which is not present in the `class` to my `XML` – BATMAN_2008 Apr 27 '21 at 16:26
  • It seems not possible to do it by jaxb unless you modify your classes, my suggestion even if ugly is manipulate the xml tree to obtain the desired result. – dariosicily Apr 28 '21 at 16:49
  • Thanks a lot for the time and response. Actually, it's possible and I was able to find a way around it. I have posted as a question here: https://stackoverflow.com/questions/67300882/jaxb-xmladapter-is-there-a-way-to-convert-this-method-into-jaxb-xmladapter Now I am trying to figure out a way to make it as `XML Adapter` rather than keeping it as a method within class. – BATMAN_2008 Apr 29 '21 at 05:26
  • Thanks for the link, in the meanwhile I have found a possible solution to your problem using an xsl file without modify the original classes, so if you are interested I submit an answer. – dariosicily Apr 29 '21 at 12:44
  • @dariosicily Yes please post it. I am really curious to know what are the other ways in which this can be achieved. Thanks a lot. – BATMAN_2008 Apr 29 '21 at 13:11

2 Answers2

1

From my point of view, it is possible to include a new tag inside your xml preserving the original classes with a combination of jaxb and javax.xml.transform api. The first thing is create a xsl stylesheet file for the transformation like below:

template.xsl

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Car>
<brand>
<xsl:value-of   select="/Car/brand"/>
</brand>
<build>
<xsl:value-of   select="/Car/build"/>
</build>
<carinfo>
<engine>
<xsl:value-of   select="/Car/engine"/>
</engine>
<year>
<xsl:value-of   select="/Car/year"/>
</year>
</carinfo>
</Car>
</xsl:template>
</xsl:stylesheet>

Then you can use the set a Transformer and pass to it a new JAXBSource like below:

Child eventInfo = jsonParser.readValueAs(Child.class);
JAXBContext context = JAXBContext.newInstance(Child.class);
           
// Create Transformer
TransformerFactory tf = TransformerFactory.newInstance();
StreamSource xslt = new StreamSource(xslFile);
Transformer transformer = tf.newTransformer(xslt);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.STANDALONE, "yes");

//Print to the stdout the desired xml with the new nested tag 
//including the two existing tags
StreamResult result = new StreamResult(System.out);
JAXBSource source = new JAXBSource(context, eventInfo);
transformer.transform(source, result);
dariosicily
  • 4,239
  • 2
  • 11
  • 17
  • 1
    Interesting approach. Thanks for the alternative way of creation. However when the XML is a bit complex and keeps changing then it can be a bit tricky to manage these files. Hence, i feel handling it within the class would be an easy approach. But this solution can be helpful for someone in the future. – BATMAN_2008 Apr 29 '21 at 15:53
  • 1
    @BATMAN_2008 You are welcome, I'm agree with you that if classes can be modifible it it more easier to use them. The XML case was simple so an xsl online generator made the required file usable without any modifies. – dariosicily Apr 29 '21 at 17:00
0

Finally, I was able to get it using the @XmlPath from Moxy which is and library based on the Jaxb. You can use the @XmlPath to any of the elements such as collection, non-collection, String etc. I have spent a lot of time trying to configure and do workarounds so posting the answer here so it can be useful to someone in the future and they do not endup so much time as me :)

Please find the answer for configuring the Moxy here

Also, see how to get the wrapper elements for the XML using the @XmlPath here

You can also read more about it in the EclipseLink documentation.

BATMAN_2008
  • 2,788
  • 3
  • 31
  • 98