DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • SmartXML: An Alternative to XPath for Complex XML Files
  • Migrate Serialized Java Objects with XStream and XMT
  • XML Processing Made Easy with Ballerina
  • How to Convert XLS to XLSX in Java

Trending

  • Medallion Architecture: Efficient Batch and Stream Processing Data Pipelines With Azure Databricks and Delta Lake
  • Understanding IEEE 802.11(Wi-Fi) Encryption and Authentication: Write Your Own Custom Packet Sniffer
  • Mastering Fluent Bit: Installing and Configuring Fluent Bit on Kubernetes (Part 3)
  • Build Your First AI Model in Python: A Beginner's Guide (1 of 3)
  1. DZone
  2. Coding
  3. Languages
  4. Migrating From JAXB XML Processing to XStream

Migrating From JAXB XML Processing to XStream

Java 9's modules have made JAXB less desirable for some projects. So let's refactor some XML processing code to use XStream instead.

By 
Alan Richardson user avatar
Alan Richardson
·
Apr. 25, 18 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
30.3K Views

Join the DZone community and get the full member experience.

Join For Free

I have a small REST API application that uses Spark and GSON and JAXB. I haven’t released this to GitHub yet, but I did release some of the example's externally executed integration verification code for it.

When trying to package this for Java 1.9, I encountered the, now standard, missing JAXB, libraries. So I thought I’d investigate another XML library.

JAXB

I used JAXB because I didn’t need to add any additional dependencies in Maven and it was bundled by default in < Java 1.8. Now that the new module system is in place, JAXB has to be included via dependencies, and this ballooned my deployable JAR file from 2.9 meg to 4.3 meg.

That might not seem like much, but I grew up with 48K and that privation leaves a permanent ‘this seems too big’ mentality.

JAXB is a big powerful set of code, and I’m only using it to serialize and deserialize a few objects.

So in reality, when I have to bundle it with my app, it is too big.

I had to add the following dependencies to my pom.xml to have JAXB functioning with Java 1.9:

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>


XStream

After a quick look around and I thought I’d try XStream.

  • XStream does not require any annotations — which is good because I have a mild aversion to annotations and this might help me remove the annotations in my payload objects.
  • XStream seems smaller as a packaged .jar
  • XStream requires a single import, to continue with JAXB I had to add four.

Not Ready Yet

My application is one that I use for training and teaching REST API testing.

Therefore:

  • It sometimes has some hacky code
  • It hasn’t been fully refactored
  • JAXB was used in quite a few places

The first thing I had to do was refactor the code to isolate the XML processing as much as possible.

I can’t do much about the JAXB annotations, since they are on the payload classes. But I can refactor out the marshalling and unmarshalling code into a single “Payload XML Parser” object.

Fortunately, I do have quite a lot of tests — far more than in the external integration test pack.

Part of the reason I use Spark is that I find it easy to spin up an instance of the app which will listen on an HTTP port so I can have HTTP tests running as part of the main project very easily.

Isolate the JAXB code

I want to isolate code like this:

try {
    JAXBContext context = JAXBContext.newInstance(ListOfListicatorListsPayload.class);
    Unmarshaller m = context.createUnmarshaller();
    ListOfListicatorListsPayload lists = (ListOfListicatorListsPayload)
                                            m.unmarshal(new StringReader(payload));
    newLists = new PayloadConvertor().transformFromPayLoadObject(lists);

} catch (Exception e) {
    e.printStackTrace();
}


Ignore the throwing away of exceptions — this is a ‘training’ app. I’m allowed to take shortcuts and have verbose console output.

And push it into something like MyXMLProcessor so that I’m creating an abstraction layer on top of the XML Payload processing, which will make it easier to test out different XML libraries if I don’t like XStream.

And so this was a simple ‘refactor to method’ approach. Which led to code like this:

try {
    newLists = new MyXmlProcessor().getListOfListicatorLists(payload);
} catch (Exception e) {
    e.printStackTrace();
}


The code behind getListOfListicatorLists at this point is exactly the same as the code that was in my PayloadConvertor.

I simply used the Find in Path to find any of the following imports, and moved the code that required the import into MyXmlProcessor as a method:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import javax.xml.bind.Marshaller;


Migrate Method by Method

I then migrated each method in turn from JAXB to XStream.

I had to do the following in response to failing tests and exceptions during the conversion.

Add an XML Header When Marshalling

When XStream marshalls an Object to XML, it doesn’t return an XML header.

So I added a quick hack rather than see if there was a configuration option in XStream. It seemed easier.

String header = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
return header + xstream.toXML(payload);


Configure in Code Rather Than Annotate

I much prefer configuring in code rather than XML files or annotations.

With XStream, this meant:

public MyXmlProcessor(){
     xstream = new XStream();
     xstream.alias("lists", ListOfListicatorListsPayload.class);
     xstream.addImplicitCollection(ListOfListicatorListsPayload.class, "lists");
     xstream.alias("list", ListicatorListPayload.class);
}


The Alias is to take the place of the JAXB annotations.

.alias

When a class name is different from the XML name, I need an alias of an annotation. 

The following is required to map a UserPayLoad to a user element.

@XmlRootElement(name = "list")
public class ListicatorListPayload {
    public String guid;
    public String title;
}


In XStream, I don’t annotate the class, I configure the XStream processor:

xstream.alias("user", UserPayload.class);


I prefer this second approach, since it should allow me to handle any special cases more easily, and it is a single place to configure everything.

The addImplicitCollection is to handle element to collection mapping.

@XmlRootElement(name = "lists")
public class ListOfListicatorListsPayload {
    @XmlElement(name="list")
    public List<ListicatorListPayload> lists = new ArrayList<>();
}


For the above, in addition to the .alias calls to map Classes to elements, I also had to map the collection to the element:

xstream.addImplicitCollection(ListOfListicatorListsPayload.class, "lists");


This happened by default in JAXB, but it seems a simple enough change for XStream.

Once all the fields were aliased, and I was using XStream to convert to and from XML, I could remove all the JAXB annotations.

XML Formatting

XStream provides nicely formatted XML. JAXB provides everything on a single line with no new lines.

This has a side-effect that it makes the API easier to work with as a training tool because the messages are formatted when viewed in a proxy without any additional parsing.

But it meant I had to change some of my assertions.

To assert on the XML generated by XStream I de-prettified it by removing all white space in the response String:

String bodyWithNoWhiteSpace = response.body.replaceAll("\\s", "");
Assert.assertTrue(
    bodyWithNoWhiteSpace.contains(
        "<user><username>user</username></user>"
       )
    );


XStream vs. JAXB

I consider the code for XStream much smaller and easier to read.

UserPayload user = (UserPayload) xstream.fromXML(payload);


vs.

try{
    JAXBContext context = JAXBContext.newInstance(UserPayload.class);
    Unmarshaller m = context.createUnmarshaller();
    return (UserPayload)m.unmarshal(new StringReader(payload));
} catch (JAXBException e) {
    e.printStackTrace();
}


And the more general form:

public <T> T simplePayloadStringConvertorConvert(String body, Class<T> theClass) {
    try {

        return (T)xstream.fromXML(body);

    }catch(Exception e){
        e.printStackTrace();
    }

    return null;
}


vs.

public <T> T simplePayloadStringConvertorConvert(String body, Class<T> theClass) {
    try {
        JAXBContext context = JAXBContext.newInstance(theClass);
        Unmarshaller m = context.createUnmarshaller();
        return (T)m.unmarshal(new StringReader(body));
    } catch (JAXBException e) {       
        e.printStackTrace();
    }

    return null;
}


TODO

I still have to configure the XStream security before I release. And I will eventually have to remove some reflection warnings.

Why

  • This forced me to refactor my code — which is always a good idea — and allowed me to isolate the XML conversion.
  • The packaged JAR was 4.3 meg with JAXB and dropped to 3.5 meg with XStream
    • Originally it was 2.9 meg when packaged against Java 1.8.
  • XStream also seems faster.

Summary of Process

  • Have tests in place to support refactoring.
  • Isolate existing XML code.
  • Read XStream 2 minute tutorial.
  • Add XStream maven dependency.
  • Convert code step by step — while running the tests
  • Done

Final Notes

I have a very small app, with minor usage of XML.

I will be converting my other XML processing apps, which also use minimal XML processing to use XStream because it was so much easier to understand and use.

Your mileage may vary.

XML XStream Processing

Published at DZone with permission of Alan Richardson, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • SmartXML: An Alternative to XPath for Complex XML Files
  • Migrate Serialized Java Objects with XStream and XMT
  • XML Processing Made Easy with Ballerina
  • How to Convert XLS to XLSX in Java

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!