| package com.fasterxml.jackson.databind.node; |
| |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.util.*; |
| |
| import com.fasterxml.jackson.annotation.JsonCreator; |
| import com.fasterxml.jackson.annotation.JsonInclude; |
| import com.fasterxml.jackson.annotation.JsonValue; |
| import com.fasterxml.jackson.databind.*; |
| import com.fasterxml.jackson.databind.annotation.JsonDeserialize; |
| import com.fasterxml.jackson.databind.exc.MismatchedInputException; |
| |
| /** |
| * Additional tests for {@link ObjectNode} container class. |
| */ |
| public class ObjectNodeTest |
| extends BaseMapTest |
| { |
| @JsonDeserialize(as = DataImpl.class) |
| public interface Data { |
| } |
| |
| public static class DataImpl implements Data |
| { |
| protected JsonNode root; |
| |
| @JsonCreator |
| public DataImpl(JsonNode n) { |
| root = n; |
| } |
| |
| @JsonValue |
| public JsonNode value() { return root; } |
| |
| /* |
| public Wrapper(ObjectNode n) { root = n; } |
| |
| @JsonValue |
| public ObjectNode value() { return root; } |
| */ |
| } |
| |
| static class ObNodeWrapper { |
| @JsonInclude(JsonInclude.Include.NON_EMPTY) |
| public ObjectNode node; |
| |
| public ObNodeWrapper(ObjectNode n) { |
| node = n; |
| } |
| } |
| |
| // [databind#941] |
| static class MyValue |
| { |
| private final ObjectNode object; |
| |
| @JsonCreator |
| public MyValue(ObjectNode object) { this.object = object; } |
| |
| @JsonValue |
| public ObjectNode getObject() { return object; } |
| } |
| |
| /* |
| /********************************************************** |
| /* Test methods |
| /********************************************************** |
| */ |
| |
| private final ObjectMapper MAPPER = objectMapper(); |
| |
| public void testSimpleObject() throws Exception |
| { |
| String JSON = "{ \"key\" : 1, \"b\" : \"x\" }"; |
| JsonNode root = MAPPER.readTree(JSON); |
| |
| // basic properties first: |
| assertFalse(root.isValueNode()); |
| assertTrue(root.isContainerNode()); |
| assertFalse(root.isArray()); |
| assertTrue(root.isObject()); |
| assertEquals(2, root.size()); |
| |
| Iterator<JsonNode> it = root.iterator(); |
| assertNotNull(it); |
| assertTrue(it.hasNext()); |
| JsonNode n = it.next(); |
| assertNotNull(n); |
| assertEquals(IntNode.valueOf(1), n); |
| |
| assertTrue(it.hasNext()); |
| n = it.next(); |
| assertNotNull(n); |
| assertEquals(TextNode.valueOf("x"), n); |
| |
| assertFalse(it.hasNext()); |
| |
| // Ok, then, let's traverse via extended interface |
| ObjectNode obNode = (ObjectNode) root; |
| Iterator<Map.Entry<String,JsonNode>> fit = obNode.fields(); |
| // we also know that LinkedHashMap is used, i.e. order preserved |
| assertTrue(fit.hasNext()); |
| Map.Entry<String,JsonNode> en = fit.next(); |
| assertEquals("key", en.getKey()); |
| assertEquals(IntNode.valueOf(1), en.getValue()); |
| |
| assertTrue(fit.hasNext()); |
| en = fit.next(); |
| assertEquals("b", en.getKey()); |
| assertEquals(TextNode.valueOf("x"), en.getValue()); |
| |
| // Plus: we should be able to modify the node via iterator too: |
| fit.remove(); |
| assertEquals(1, obNode.size()); |
| assertEquals(IntNode.valueOf(1), root.get("key")); |
| assertNull(root.get("b")); |
| } |
| // for [databind#346] |
| public void testEmptyNodeAsValue() throws Exception |
| { |
| Data w = MAPPER.readValue("{}", Data.class); |
| assertNotNull(w); |
| } |
| |
| public void testBasics() |
| { |
| ObjectNode n = new ObjectNode(JsonNodeFactory.instance); |
| assertStandardEquals(n); |
| |
| assertFalse(n.elements().hasNext()); |
| assertFalse(n.fields().hasNext()); |
| assertFalse(n.fieldNames().hasNext()); |
| assertNull(n.get("a")); |
| assertTrue(n.path("a").isMissingNode()); |
| |
| TextNode text = TextNode.valueOf("x"); |
| assertSame(n, n.set("a", text)); |
| |
| assertEquals(1, n.size()); |
| assertTrue(n.elements().hasNext()); |
| assertTrue(n.fields().hasNext()); |
| assertTrue(n.fieldNames().hasNext()); |
| assertSame(text, n.get("a")); |
| assertSame(text, n.path("a")); |
| assertNull(n.get("b")); |
| assertNull(n.get(0)); // not used with objects |
| |
| assertFalse(n.has(0)); |
| assertFalse(n.hasNonNull(0)); |
| assertTrue(n.has("a")); |
| assertTrue(n.hasNonNull("a")); |
| assertFalse(n.has("b")); |
| assertFalse(n.hasNonNull("b")); |
| |
| ObjectNode n2 = new ObjectNode(JsonNodeFactory.instance); |
| n2.put("b", 13); |
| assertFalse(n.equals(n2)); |
| n.setAll(n2); |
| |
| assertEquals(2, n.size()); |
| n.set("null", (JsonNode)null); |
| assertEquals(3, n.size()); |
| // may be non-intuitive, but explicit nulls do exist in tree: |
| assertTrue(n.has("null")); |
| assertFalse(n.hasNonNull("null")); |
| // should replace, not add |
| n.put("null", "notReallNull"); |
| assertEquals(3, n.size()); |
| assertNotNull(n.remove("null")); |
| assertEquals(2, n.size()); |
| |
| Map<String,JsonNode> nodes = new HashMap<String,JsonNode>(); |
| nodes.put("d", text); |
| n.setAll(nodes); |
| assertEquals(3, n.size()); |
| |
| n.removeAll(); |
| assertEquals(0, n.size()); |
| } |
| |
| public void testBigNumbers() |
| { |
| ObjectNode n = new ObjectNode(JsonNodeFactory.instance); |
| assertStandardEquals(n); |
| BigInteger I = BigInteger.valueOf(3); |
| BigDecimal DEC = new BigDecimal("0.1"); |
| |
| n.put("a", DEC); |
| n.put("b", I); |
| |
| assertEquals(2, n.size()); |
| |
| assertTrue(n.path("a").isBigDecimal()); |
| assertEquals(DEC, n.get("a").decimalValue()); |
| assertTrue(n.path("b").isBigInteger()); |
| assertEquals(I, n.get("b").bigIntegerValue()); |
| } |
| |
| /** |
| * Verify null handling |
| */ |
| public void testNullChecking() |
| { |
| ObjectNode o1 = JsonNodeFactory.instance.objectNode(); |
| ObjectNode o2 = JsonNodeFactory.instance.objectNode(); |
| // used to throw NPE before fix: |
| o1.setAll(o2); |
| assertEquals(0, o1.size()); |
| assertEquals(0, o2.size()); |
| |
| // also: nulls should be converted to NullNodes... |
| o1.set("x", null); |
| JsonNode n = o1.get("x"); |
| assertNotNull(n); |
| assertSame(n, NullNode.instance); |
| |
| o1.put("str", (String) null); |
| n = o1.get("str"); |
| assertNotNull(n); |
| assertSame(n, NullNode.instance); |
| |
| o1.put("d", (BigDecimal) null); |
| n = o1.get("d"); |
| assertNotNull(n); |
| assertSame(n, NullNode.instance); |
| |
| o1.put("3", (BigInteger) null); |
| n = o1.get("3"); |
| assertNotNull(3); |
| assertSame(n, NullNode.instance); |
| |
| assertEquals(4, o1.size()); |
| } |
| |
| /** |
| * Another test to verify [JACKSON-227]... |
| */ |
| public void testNullChecking2() |
| { |
| ObjectNode src = MAPPER.createObjectNode(); |
| ObjectNode dest = MAPPER.createObjectNode(); |
| src.put("a", "b"); |
| dest.setAll(src); |
| } |
| |
| public void testRemove() |
| { |
| ObjectNode ob = MAPPER.createObjectNode(); |
| ob.put("a", "a"); |
| ob.put("b", "b"); |
| ob.put("c", "c"); |
| assertEquals(3, ob.size()); |
| assertSame(ob, ob.without(Arrays.asList("a", "c"))); |
| assertEquals(1, ob.size()); |
| assertEquals("b", ob.get("b").textValue()); |
| } |
| |
| public void testRetain() |
| { |
| ObjectNode ob = MAPPER.createObjectNode(); |
| ob.put("a", "a"); |
| ob.put("b", "b"); |
| ob.put("c", "c"); |
| assertEquals(3, ob.size()); |
| assertSame(ob, ob.retain("a", "c")); |
| assertEquals(2, ob.size()); |
| assertEquals("a", ob.get("a").textValue()); |
| assertNull(ob.get("b")); |
| assertEquals("c", ob.get("c").textValue()); |
| } |
| |
| public void testValidWith() throws Exception |
| { |
| ObjectNode root = MAPPER.createObjectNode(); |
| assertEquals("{}", MAPPER.writeValueAsString(root)); |
| JsonNode child = root.with("prop"); |
| assertTrue(child instanceof ObjectNode); |
| assertEquals("{\"prop\":{}}", MAPPER.writeValueAsString(root)); |
| } |
| |
| public void testValidWithArray() throws Exception |
| { |
| ObjectNode root = MAPPER.createObjectNode(); |
| assertEquals("{}", MAPPER.writeValueAsString(root)); |
| JsonNode child = root.withArray("arr"); |
| assertTrue(child instanceof ArrayNode); |
| assertEquals("{\"arr\":[]}", MAPPER.writeValueAsString(root)); |
| } |
| |
| public void testInvalidWith() throws Exception |
| { |
| JsonNode root = MAPPER.createArrayNode(); |
| try { // should not work for non-ObjectNode nodes: |
| root.with("prop"); |
| fail("Expected exception"); |
| } catch (UnsupportedOperationException e) { |
| verifyException(e, "not of type ObjectNode"); |
| } |
| // also: should fail of we already have non-object property |
| ObjectNode root2 = MAPPER.createObjectNode(); |
| root2.put("prop", 13); |
| try { // should not work for non-ObjectNode nodes: |
| root2.with("prop"); |
| fail("Expected exception"); |
| } catch (UnsupportedOperationException e) { |
| verifyException(e, "has value that is not"); |
| } |
| } |
| |
| public void testInvalidWithArray() throws Exception |
| { |
| JsonNode root = MAPPER.createArrayNode(); |
| try { // should not work for non-ObjectNode nodes: |
| root.withArray("prop"); |
| fail("Expected exception"); |
| } catch (UnsupportedOperationException e) { |
| verifyException(e, "not of type ObjectNode"); |
| } |
| // also: should fail of we already have non-Array property |
| ObjectNode root2 = MAPPER.createObjectNode(); |
| root2.put("prop", 13); |
| try { // should not work for non-ObjectNode nodes: |
| root2.withArray("prop"); |
| fail("Expected exception"); |
| } catch (UnsupportedOperationException e) { |
| verifyException(e, "has value that is not"); |
| } |
| } |
| |
| // [Issue#93] |
| public void testSetAll() throws Exception |
| { |
| ObjectNode root = MAPPER.createObjectNode(); |
| assertEquals(0, root.size()); |
| HashMap<String,JsonNode> map = new HashMap<String,JsonNode>(); |
| map.put("a", root.numberNode(1)); |
| root.setAll(map); |
| assertEquals(1, root.size()); |
| assertTrue(root.has("a")); |
| assertFalse(root.has("b")); |
| |
| map.put("b", root.numberNode(2)); |
| root.setAll(map); |
| assertEquals(2, root.size()); |
| assertTrue(root.has("a")); |
| assertTrue(root.has("b")); |
| assertEquals(2, root.path("b").intValue()); |
| |
| // Then with ObjectNodes... |
| ObjectNode root2 = MAPPER.createObjectNode(); |
| root2.setAll(root); |
| assertEquals(2, root.size()); |
| assertEquals(2, root2.size()); |
| |
| root2.setAll(root); |
| assertEquals(2, root.size()); |
| assertEquals(2, root2.size()); |
| |
| ObjectNode root3 = MAPPER.createObjectNode(); |
| root3.put("a", 2); |
| root3.put("c", 3); |
| assertEquals(2, root3.path("a").intValue()); |
| root3.setAll(root2); |
| assertEquals(3, root3.size()); |
| assertEquals(1, root3.path("a").intValue()); |
| } |
| |
| // [databind#237] (databind): support DeserializationFeature#FAIL_ON_READING_DUP_TREE_KEY |
| public void testFailOnDupKeys() throws Exception |
| { |
| final String DUP_JSON = "{ \"a\":1, \"a\":2 }"; |
| |
| // first: verify defaults: |
| ObjectMapper mapper = new ObjectMapper(); |
| assertFalse(mapper.isEnabled(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY)); |
| ObjectNode root = (ObjectNode) mapper.readTree(DUP_JSON); |
| assertEquals(2, root.path("a").asInt()); |
| |
| // and then enable checks: |
| try { |
| mapper.reader(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY).readTree(DUP_JSON); |
| fail("Should have thrown exception!"); |
| } catch (JsonMappingException e) { |
| verifyException(e, "duplicate field 'a'"); |
| } |
| } |
| |
| public void testEqualityWrtOrder() throws Exception |
| { |
| ObjectNode ob1 = MAPPER.createObjectNode(); |
| ObjectNode ob2 = MAPPER.createObjectNode(); |
| |
| // same contents, different insertion order; should not matter |
| |
| ob1.put("a", 1); |
| ob1.put("b", 2); |
| ob1.put("c", 3); |
| |
| ob2.put("b", 2); |
| ob2.put("c", 3); |
| ob2.put("a", 1); |
| |
| assertTrue(ob1.equals(ob2)); |
| assertTrue(ob2.equals(ob1)); |
| } |
| |
| public void testSimplePath() throws Exception |
| { |
| JsonNode root = MAPPER.readTree("{ \"results\" : { \"a\" : 3 } }"); |
| assertTrue(root.isObject()); |
| JsonNode rnode = root.path("results"); |
| assertNotNull(rnode); |
| assertTrue(rnode.isObject()); |
| assertEquals(3, rnode.path("a").intValue()); |
| } |
| |
| public void testNonEmptySerialization() throws Exception |
| { |
| ObNodeWrapper w = new ObNodeWrapper(MAPPER.createObjectNode() |
| .put("a", 3)); |
| assertEquals("{\"node\":{\"a\":3}}", MAPPER.writeValueAsString(w)); |
| w = new ObNodeWrapper(MAPPER.createObjectNode()); |
| assertEquals("{}", MAPPER.writeValueAsString(w)); |
| } |
| |
| public void testIssue941() throws Exception |
| { |
| ObjectNode object = MAPPER.createObjectNode(); |
| |
| String json = MAPPER.writeValueAsString(object); |
| // System.out.println("json: "+json); |
| |
| ObjectNode de1 = MAPPER.readValue(json, ObjectNode.class); // this works |
| // System.out.println("Deserialized to ObjectNode: "+de1); |
| assertNotNull(de1); |
| |
| MyValue de2 = MAPPER.readValue(json, MyValue.class); // but this throws exception |
| // System.out.println("Deserialized to MyValue: "+de2); |
| assertNotNull(de2); |
| } |
| |
| public void testSimpleMismatch() throws Exception |
| { |
| ObjectMapper mapper = objectMapper(); |
| try { |
| mapper.readValue("[ 1, 2, 3 ]", ObjectNode.class); |
| fail("Should not pass"); |
| } catch (MismatchedInputException e) { |
| verifyException(e, "out of START_ARRAY token"); |
| } |
| } |
| } |