Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Jackson to add XML attributes to manually-built node-tree

I'm trying to set up code to create a node tree using Jackson which can then be used to write either JSON or XML. I've created the node tree manually like so:

XmlMapper nodeMapper = new XmlMapper();

ObjectNode rootNode = nodeMapper.createObjectNode();
ObjectNode currentNode = rootNode.putObject("Examples");
currentNode.put("Puppy", TRUE)
           .put("Apple", 2)
           .put("Jet", "Li");
currentNode = rootNode.putObject("Single");
currentNode.put("One", 1);

String writePath = "C:/users/itsameamario/Documents/basicXMLtest.xml";
nodeMapper.writeValue(new File(writePath), rootNode);

My XML output is:

<?xml version="1.0"?>
<ObjectNode>
    <Examples>
        <Puppy>true</Puppy>
        <Apple>2</Apple>
        <Jet>Li</Jet>
    </Examples>
    <Single>
        <One>1</One>
    </Single>
</ObjectNode>

However for some parts of the XML I would like to add an attribute to one of the nodes like so:

<Examples overlyComplicated="yes">
<!--...-->
</Examples>

All the examples I've found that include attributes are applied to a pre-existing class. I have been unable to find a method to add attributes to a manually-built node-tree as above. Is it doable using Jackson?

like image 962
Alexander Avatar asked Jul 16 '19 15:07

Alexander


People also ask

What is @jacksonxmlrootelement and how do I use it?

The @JacksonXmlRootElement has a similar role to the @JacksonXmlProperty but for the root element of the entire document. This can only adjust the Namespace and Local name – since the root element can never be serialized as an attribute. For example, let’s look at this Java POJO: When serialized, this will result in the following XML:

What is the use of annotation @jacksonxmlproperty?

Mapping XML Property (Attribute / Element) Annotation @JacksonXmlProperty can be used to provide XML-specific configuration for properties, above and beyond what @JsonProperty contains. It is an alternative to using JAXB annotations. Using boolean property isAttribute can control if the target property is attribute or XML element.

How do I use Jackson to read an XML file?

Jackson also allows us to read the contents of an XML file and deserialize the XML String back into a Java object. In our example, we will read an XML document containing details about a phone, and use Jackson to extract this data and use it to create Java objects containing the same information.

What is the Jackson XML module?

Summary If you’re looking for a mature, flexible way of supporting and working with both JSON and XML for the same data, the Jackson XML module is a fantastic library to leverage. It’s not only a solid way to go on its own, but it also has the added benefit of being able to mostly reuse the same configuration for both XML as well as JSON.


1 Answers

It is not possible to mark given property as attribute since ObjectNode does not know anything about the serialisation. You can do that for POJO class and com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator will handle it only if @JacksonXmlProperty(isAttribute = true) annotation is used for given property. I suggest to create POJO for element where you need attribute and use Jackson XML annotations or implement JsonSerializable interface. It could look like below:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializable;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

public class XmlMapperApp {

    public static void main(String[] args) throws Exception {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("Puppy", Boolean.TRUE);
        map.put("Apple", 2);
        map.put("Jet", "Li");
        Examples examples = new Examples();
        examples.setOverlyComplicated("yes");
        examples.setMap(map);

        XmlMapper mapper = new XmlMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        ObjectNode rootNode = mapper.createObjectNode();
        rootNode.putPOJO("Examples", examples);
        ObjectNode currentNode = rootNode.putObject("Single");
        currentNode.put("One", 1);

        mapper.writeValue(System.out, rootNode);
    }
}

class Examples implements JsonSerializable {

    @Override
    public void serialize(JsonGenerator gen, SerializerProvider serializers) throws IOException {
        ToXmlGenerator toXmlGenerator = (ToXmlGenerator) gen;
        toXmlGenerator.writeStartObject();

        writeAttributes(toXmlGenerator);
        writeMap(toXmlGenerator);

        toXmlGenerator.writeEndObject();
    }

    private void writeAttributes(ToXmlGenerator gen) throws IOException {
        if (overlyComplicated != null) {
            gen.setNextIsAttribute(true);
            gen.writeFieldName("overlyComplicated");
            gen.writeString(overlyComplicated);
            gen.setNextIsAttribute(false);
        }
    }

    private void writeMap(ToXmlGenerator toXmlGenerator) throws IOException {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            toXmlGenerator.writeObjectField(entry.getKey(), entry.getValue());
        }
    }

    @Override
    public void serializeWithType(JsonGenerator gen, SerializerProvider serializers, TypeSerializer typeSer) throws IOException {
        serialize(gen, serializers);
    }

    private String overlyComplicated;
    private Map<String, Object> map;

    // getters, setters, toString
}

Above code prints:

<ObjectNode>
  <Examples overlyComplicated="yes">
    <Puppy>true</Puppy>
    <Apple>2</Apple>
    <Jet>Li</Jet>
  </Examples>
  <Single>
    <One>1</One>
  </Single>
</ObjectNode>

In case you want to use the same Example POJO for JSON serialisation you need to handle it in serialize method or create another ObjectNode instead of Examlples object.

like image 148
Michał Ziober Avatar answered Oct 16 '22 07:10

Michał Ziober