Constructing an XML Payload in Java for an API Call

I offer a solution to a problem with the Java jackson-dataformat-xml library where the .writeValueAsString() function wraps the desired XML in <ObjectNode> tags.

Constructing an XML Payload in Java for an API Call

It has been a while since I've written a software engineering blog post because a lot of other things have been on my mind like the pandemic, California fires, and the elections. Now that the elections have completed, I'm going to continue writing about the software engineering topics that have been on my queue.

Recently I was working on a Java microservice that needed to make an HTTP PUT request with an XML payload. This API in particular is still on a classic version which only accepts XML payloads, and its newer API which takes JSON's is in beta. In short, we're stuck using the XML payloads for now. For other API calls I usually use the Java Jackson databind library to construct JSON payloads, then send the serialized JSON - meaning as a String - to the API. Following this same approach, I want to use the XmlMapper class from a databind library called jackson-dataformat-xml.

Problem

I want to send this XML payload to the API:

<computer>
    <general>
    	<name>
            computer-name-placeholder
        </name>
    </general>
</computer>
Desired XML payload

The problem is that after constructing the ObjectNode and converting it to a String, I notice an <ObjectNode></ObjectNode> tag placed around the desired XML. As a result, when I passed this serialized XML payload to the API call, the servers returned "Invalid API File." See the code snippet below.

dependencies {
	compile group: "com.fasterxml.jackson.dataformat", name: "jackson-dataformat-xml", version: "2.10.1"
}
build.gradle to import library
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

private final XmlMapper xmlMapper = new XmlMapper();

// This constructs the XML payload.
ObjectNode rootNode = xmlMapper.createObjectNode();
ObjectNode computerNode = rootNode.putObject("computer");
ObjectNode generalNode = computerNode.putObject("general");
generalNode.put("name", "computer-name-placeholder");

String payload = writer.writeValueAsString(rootNode);
// payload looks like this: <ObjectNode><computer><general><name>computer-name-placeholder</name></general></computer></ObjectNode>
Notice the wrapped <ObjectNode> tags.

I also tried using rootNode.toString() but that only returned a serialized Json object.

Solution

Alexander's answer on this Stack Overflow post solved the problem. Here's my code:

private final XmlMapper xmlMapper = new XmlMapper();

ObjectWriter writer = xmlMapper.writer().withRootName("computer");
ObjectNode computerNode = xmlMapper.createObjectNode();
ObjectNode generalNode = computerNode.putObject("general");
generalNode.put("name", "computer-name-placeholder");

String payload = writer.writeValueAsString(computerNode);
// payload looks like this: <computer><general><name>computer-name-placeholder</name></general></computer>
Working solution. The key difference is using the ObjectWriter's withRootName function.

The main difference here is that the ObjectNode rootNode has been replaced with ObjectWriter writer. The key to removing the <ObjectNode> tag is to use the function .withRootName("computer").  With this code, I was able to stringify the XML payload and complete the PUT API request successfully.

Summary

I spent nearly 2 hours to solve this very simple problem; oddly there wasn't much information on it, not even a single Github issue referencing the <ObjectNode> tags. I hope you come across this article if you are stuck like I was, and that it helps you save time.