ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.createObjectNode();
or you can also do like you said in the comment above,
JsonNode node = JsonNodeFactory.instance.objectNode();
after that you can map the values,
JsonNode node = mapper.valueToTree(fromValue);
Answer from sanurah on Stack OverflowVideos
These methods are in ObjectNode: the division is such that most read operations are included in JsonNode, but mutations in ObjectNode and ArrayNode.
Note that you can just change first line to be:
ObjectNode jNode = mapper.createObjectNode();
// version ObjectMapper has should return ObjectNode type
or
ObjectNode jNode = (ObjectNode) objectCodec.createObjectNode();
// ObjectCodec is in core part, must be of type JsonNode so need cast
I've recently found even more interesting way to create any ValueNode or ContainerNode (Jackson v2.3).
ObjectNode node = JsonNodeFactory.instance.objectNode();
I figured out that this behaviour can be achieved via configuration. Here is the kotlin code but it's simple to convert to java Just create xmlMapper with appropriate configuration
fun jacksonCreateXmlMapper(): XmlMapper {
val module = JacksonXmlModule()
module.setXMLTextElementName("value")
return XmlMapper(module)
}
For input
<products>
<product count="5">apple</product>
<product count="10">orange</product>
</products>
you get:
{
"product" : [ {
"count" : "5",
"value" : "apple"
}, {
"count" : "10",
"value" : "orange"
} ]
}
You also could simply post-process the JSON DOM, traverse to all objects, and rename the keys that are empty strings to "value".
Race condition: such a key may already exist, and must not be overwritten
(e.g. <id type="pid" value="existing">abcdef123</id>).
Usage:
(note: you should not silently suppress the exception and return null, but allow it to propagate so the caller can decide to catch and apply failover logic if required)
public InputStream parseXmlResponse(InputStream xmlStream) throws IOException {
JsonNode node = xmlMapper.readTree(xmlStream);
postprocess(node);
return new ByteArrayInputStream(jsonMapper.writer().writeValueAsBytes(node));
}
Post-processing:
private void postprocess(JsonNode jsonNode) {
if (jsonNode.isArray()) {
ArrayNode array = (ArrayNode) jsonNode;
Iterable<JsonNode> elements = () -> array.elements();
// recursive post-processing
for (JsonNode element : elements) {
postprocess(element);
}
}
if (jsonNode.isObject()) {
ObjectNode object = (ObjectNode) jsonNode;
Iterable<String> fieldNames = () -> object.fieldNames();
// recursive post-processing
for (String fieldName : fieldNames) {
postprocess(object.get(fieldName));
}
// check if an attribute with empty string key exists, and rename it to 'value',
// unless there already exists another non-null attribute named 'value' which
// would be overwritten.
JsonNode emptyKeyValue = object.get("");
JsonNode existing = object.get("value");
if (emptyKeyValue != null) {
if (existing == null || existing.isNull()) {
object.set("value", emptyKeyValue);
object.remove("");
} else {
System.err.println("Skipping empty key value as a key named 'value' already exists.");
}
}
}
}
Output: just as expected.
{
"elementName": {
"id": {
"type": "pid",
"value": "abcdef123"
}
},
}
EDIT: considerations on performance:
I did a test with a large XML file (enwikiquote-20200520-pages-articles-multistream.xml, en.wikiquote XML dump, 498.4 MB), 100 rounds, with following measured times (using deltas with System.nanoTime()):
- average read time (File, SSD): 2870.96 ms
(JsonNode node = xmlMapper.readTree(xmlStream);) - average postprocessing time: 0.04 ms
(postprocess(node);) - average write time (memory): 0.31 ms
(new ByteArrayInputStream(jsonMapper.writer().writeValueAsBytes(node));)
That's a fraction of a millisecond for an object tree build from a ~500 MB file - so performance is excellent and no concern.
This works, although intention is that it's factory that creates instances. But most commonly you just access all of it using ObjectMapper, like:
ObjectMapper mapper = new ObjectMapper();
ObjectNode dataTable = mapper.createObjectNode();
ArrayNode aa = dataTable.putArray("aaData");
The main reason for separate JsonNodeFactory is to allow you to create custom node types (usually sub-classes of standard instances); and then configure ObjectMapper to use different factory. For convenience, ArrayNode and ObjectNode do have reference to a factory instance, which is used with "putArray" and other methods that need to create new nodes.
If you do a lot of JsonNode building in code, you may be interesting in the following set of utilities. The benefit of using them is that they support a more natural chaining style that better shows the structure of the JSON under contruction.
Here is an example usage:
import static JsonNodeBuilders.array;
import static JsonNodeBuilders.object;
...
val request = object("x", "1").with("y", array(object("z", "2"))).end();
Which is equivalent to the following JSON:
{"x":"1", "y": [{"z": "2"}]}
Here are the classes:
import static lombok.AccessLevel.PRIVATE;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.val;
/**
* Convenience {@link JsonNode} builder.
*/
@NoArgsConstructor(access = PRIVATE)
public final class JsonNodeBuilders {
/**
* Factory methods for an {@link ObjectNode} builder.
*/
public static ObjectNodeBuilder object() {
return object(JsonNodeFactory.instance);
}
public static ObjectNodeBuilder object(@NonNull String k1, boolean v1) {
return object().with(k1, v1);
}
public static ObjectNodeBuilder object(@NonNull String k1, int v1) {
return object().with(k1, v1);
}
public static ObjectNodeBuilder object(@NonNull String k1, float v1) {
return object().with(k1, v1);
}
public static ObjectNodeBuilder object(@NonNull String k1, String v1) {
return object().with(k1, v1);
}
public static ObjectNodeBuilder object(@NonNull String k1, String v1, @NonNull String k2, String v2) {
return object(k1, v1).with(k2, v2);
}
public static ObjectNodeBuilder object(@NonNull String k1, String v1, @NonNull String k2, String v2,
@NonNull String k3, String v3) {
return object(k1, v1, k2, v2).with(k3, v3);
}
public static ObjectNodeBuilder object(@NonNull String k1, JsonNodeBuilder<?> builder) {
return object().with(k1, builder);
}
public static ObjectNodeBuilder object(JsonNodeFactory factory) {
return new ObjectNodeBuilder(factory);
}
/**
* Factory methods for an {@link ArrayNode} builder.
*/
public static ArrayNodeBuilder array() {
return array(JsonNodeFactory.instance);
}
public static ArrayNodeBuilder array(@NonNull boolean... values) {
return array().with(values);
}
public static ArrayNodeBuilder array(@NonNull int... values) {
return array().with(values);
}
public static ArrayNodeBuilder array(@NonNull String... values) {
return array().with(values);
}
public static ArrayNodeBuilder array(@NonNull JsonNodeBuilder<?>... builders) {
return array().with(builders);
}
public static ArrayNodeBuilder array(JsonNodeFactory factory) {
return new ArrayNodeBuilder(factory);
}
public interface JsonNodeBuilder<T extends JsonNode> {
/**
* Construct and return the {@link JsonNode} instance.
*/
T end();
}
@RequiredArgsConstructor
private static abstract class AbstractNodeBuilder<T extends JsonNode> implements JsonNodeBuilder<T> {
/**
* The source of values.
*/
@NonNull
protected final JsonNodeFactory factory;
/**
* The value under construction.
*/
@NonNull
protected final T node;
/**
* Returns a valid JSON string, so long as {@code POJONode}s not used.
*/
@Override
public String toString() {
return node.toString();
}
}
public final static class ObjectNodeBuilder extends AbstractNodeBuilder<ObjectNode> {
private ObjectNodeBuilder(JsonNodeFactory factory) {
super(factory, factory.objectNode());
}
public ObjectNodeBuilder withNull(@NonNull String field) {
return with(field, factory.nullNode());
}
public ObjectNodeBuilder with(@NonNull String field, int value) {
return with(field, factory.numberNode(value));
}
public ObjectNodeBuilder with(@NonNull String field, float value) {
return with(field, factory.numberNode(value));
}
public ObjectNodeBuilder with(@NonNull String field, boolean value) {
return with(field, factory.booleanNode(value));
}
public ObjectNodeBuilder with(@NonNull String field, String value) {
return with(field, factory.textNode(value));
}
public ObjectNodeBuilder with(@NonNull String field, JsonNode value) {
node.set(field, value);
return this;
}
public ObjectNodeBuilder with(@NonNull String field, @NonNull JsonNodeBuilder<?> builder) {
return with(field, builder.end());
}
public ObjectNodeBuilder withPOJO(@NonNull String field, @NonNull Object pojo) {
return with(field, factory.pojoNode(pojo));
}
@Override
public ObjectNode end() {
return node;
}
}
public final static class ArrayNodeBuilder extends AbstractNodeBuilder<ArrayNode> {
private ArrayNodeBuilder(JsonNodeFactory factory) {
super(factory, factory.arrayNode());
}
public ArrayNodeBuilder with(boolean value) {
node.add(value);
return this;
}
public ArrayNodeBuilder with(@NonNull boolean... values) {
for (val value : values)
with(value);
return this;
}
public ArrayNodeBuilder with(int value) {
node.add(value);
return this;
}
public ArrayNodeBuilder with(@NonNull int... values) {
for (val value : values)
with(value);
return this;
}
public ArrayNodeBuilder with(float value) {
node.add(value);
return this;
}
public ArrayNodeBuilder with(String value) {
node.add(value);
return this;
}
public ArrayNodeBuilder with(@NonNull String... values) {
for (val value : values)
with(value);
return this;
}
public ArrayNodeBuilder with(@NonNull Iterable<String> values) {
for (val value : values)
with(value);
return this;
}
public ArrayNodeBuilder with(JsonNode value) {
node.add(value);
return this;
}
public ArrayNodeBuilder with(@NonNull JsonNode... values) {
for (val value : values)
with(value);
return this;
}
public ArrayNodeBuilder with(JsonNodeBuilder<?> value) {
return with(value.end());
}
public ArrayNodeBuilder with(@NonNull JsonNodeBuilder<?>... builders) {
for (val builder : builders)
with(builder);
return this;
}
@Override
public ArrayNode end() {
return node;
}
}
}
Note that the implementation uses Lombok, but you can easily desugar it to fill in the Java boilerplate.