Skip to content

Commit

Permalink
Fixed #18
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Jun 5, 2019
1 parent efa6044 commit 92ca9f4
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 12 deletions.
1 change: 1 addition & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Project: jackson-databind

2.10.0 (not yet released)

#18: Make `ObjectNode` and `ArrayNode` serializable
#1675: Remove "impossible" `IOException` in `readTree()` and `readValue()` `ObjectMapper`
methods which accept Strings
(requested by matthew-pwnieexpress@github)
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
*/
public class ArrayNode
extends ContainerNode<ArrayNode>
implements java.io.Serializable // since 2.10
{
private static final long serialVersionUID = 1L;

private final List<JsonNode> _children;

public ArrayNode(JsonNodeFactory nf) {
Expand Down Expand Up @@ -842,6 +845,17 @@ public int hashCode() {
return _children.hashCode();
}

/*
/**********************************************************
/* JDK Serialization support
/**********************************************************
*/

// Simplest way is by using a helper
Object writeReplace() {
return NodeSerialization.from(this);
}

/*
/**********************************************************
/* Internal methods (overridable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
*/
public abstract class BaseJsonNode
extends JsonNode
implements JsonSerializable
{
protected BaseJsonNode() { }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ protected ContainerNode(JsonNodeFactory nc) {
_nodeFactory = nc;
}

protected ContainerNode() { _nodeFactory = null; } // only for JDK ser

// all containers are mutable: can't define:
// @Override public abstract <T extends JsonNode> T deepCopy();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.IOException;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.json.JsonMapper;

Expand All @@ -15,10 +16,15 @@
*/
final class InternalNodeMapper {
private final static JsonMapper JSON_MAPPER = new JsonMapper();

private final static ObjectWriter STD_WRITER = JSON_MAPPER.writer();
private final static ObjectWriter PRETTY_WRITER = JSON_MAPPER.writer()
.withDefaultPrettyPrinter();

private final static ObjectReader NODE_READER = JSON_MAPPER.readerFor(JsonNode.class);

// // // Methods for `JsonNode.toString()` and `JsonNode.toPrettyString()`

public static String nodeToString(JsonNode n) {
try {
return STD_WRITER.writeValueAsString(n);
Expand All @@ -34,4 +40,14 @@ public static String nodeToPrettyString(JsonNode n) {
throw new RuntimeException(e);
}
}

// // // Methods for JDK serialization support of JsonNodes

public static byte[] valueToBytes(Object value) throws IOException {
return JSON_MAPPER.writeValueAsBytes(value);
}

public static JsonNode bytesToNode(byte[] json) throws IOException {
return NODE_READER.readValue(json);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.fasterxml.jackson.databind.node;

import java.io.IOException;

/**
* Helper value class only used during JDK serialization: contains JSON as `byte[]`
*
* @since 2.10
*/
class NodeSerialization implements java.io.Serializable {
private static final long serialVersionUID = 1L;

public byte[] json;

public NodeSerialization(byte[] b) { json = b; }

protected Object readResolve() {
try {
return InternalNodeMapper.bytesToNode(json);
} catch (IOException e) {
throw new IllegalArgumentException("Failed to JDK deserialize `JsonNode` value: "+e.getMessage(), e);
}
}

public static NodeSerialization from(Object o) {
try {
return new NodeSerialization(InternalNodeMapper.valueToBytes(o));
} catch (IOException e) {
throw new IllegalArgumentException("Failed to JDK serialize `"+o.getClass().getSimpleName()+"` value: "+e.getMessage(), e);
}
}
}
24 changes: 19 additions & 5 deletions src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
package com.fasterxml.jackson.databind.node;

import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.type.WritableTypeId;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.util.RawValue;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

/**
* Node that maps to JSON Object structures in JSON content.
*<p>
* Note: class was <code>final</code> temporarily for Jackson 2.2.
*/
public class ObjectNode
extends ContainerNode<ObjectNode>
implements java.io.Serializable
{
private static final long serialVersionUID = 1L; // since 2.10

// Note: LinkedHashMap for backwards compatibility
protected final Map<String, JsonNode> _children;

Expand Down Expand Up @@ -872,6 +875,17 @@ public int hashCode()
return _children.hashCode();
}

/*
/**********************************************************
/* JDK Serialization support
/**********************************************************
*/

// Simplest way is by using a helper
Object writeReplace() {
return NodeSerialization.from(this);
}

/*
/**********************************************************
/* Internal methods (overridable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.LRUMap;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.fasterxml.jackson.databind;

import java.io.*;

import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class TestNodeJDKSerialization extends BaseMapTest
{
private final ObjectMapper MAPPER = newObjectMapper();

/*
/**********************************************************
/* Then something bit different; serialize `JsonNode`(s)
/**********************************************************
*/

// [databind#18]: Allow JDK serialization of `ObjectNode`
public void testObjectNodeSerialization() throws Exception
{
ObjectNode root = MAPPER.createObjectNode();
root.put("answer", 42);
ArrayNode arr = root.withArray("matrix");
arr.add(1).add(12345678901L).add(true).add("...");
ObjectNode misc = root.with("misc");
misc.put("value", 0.25);

byte[] ser = jdkSerialize(root);
JsonNode result = jdkDeserialize(ser);
assertEquals(root, result);
}

// [databind#18]: Allow JDK serialization of `ArrayNode`
public void testArrayNodeSerialization() throws Exception
{
ArrayNode root = MAPPER.createArrayNode();
root.add(false);
ObjectNode props = root.addObject();
props.put("answer", 42);
root.add(137);

byte[] ser = jdkSerialize(root);
JsonNode result = jdkDeserialize(ser);
assertEquals(root, result);
}

/*
/**********************************************************
/* Helper methods
/**********************************************************
*/

protected byte[] jdkSerialize(Object o) throws IOException
{
ByteArrayOutputStream bytes = new ByteArrayOutputStream(1000);
ObjectOutputStream obOut = new ObjectOutputStream(bytes);
obOut.writeObject(o);
obOut.close();
return bytes.toByteArray();
}

@SuppressWarnings("unchecked")
protected <T> T jdkDeserialize(byte[] raw) throws IOException
{
ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(raw));
try {
return (T) objIn.readObject();
} catch (ClassNotFoundException e) {
fail("Missing class: "+e.getMessage());
return null;
} finally {
objIn.close();
}
}
}

0 comments on commit 92ca9f4

Please sign in to comment.