1 package com.fasterxml.jackson.databind.node; 2 3 import java.math.BigDecimal; 4 import java.math.BigInteger; 5 import java.util.*; 6 7 import com.fasterxml.jackson.annotation.JsonCreator; 8 import com.fasterxml.jackson.annotation.JsonInclude; 9 import com.fasterxml.jackson.annotation.JsonValue; 10 import com.fasterxml.jackson.databind.*; 11 import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 12 import com.fasterxml.jackson.databind.exc.MismatchedInputException; 13 14 /** 15 * Additional tests for {@link ObjectNode} container class. 16 */ 17 public class ObjectNodeTest 18 extends BaseMapTest 19 { 20 @JsonDeserialize(as = DataImpl.class) 21 public interface Data { 22 } 23 24 public static class DataImpl implements Data 25 { 26 protected JsonNode root; 27 28 @JsonCreator DataImpl(JsonNode n)29 public DataImpl(JsonNode n) { 30 root = n; 31 } 32 33 @JsonValue value()34 public JsonNode value() { return root; } 35 36 /* 37 public Wrapper(ObjectNode n) { root = n; } 38 39 @JsonValue 40 public ObjectNode value() { return root; } 41 */ 42 } 43 44 static class ObNodeWrapper { 45 @JsonInclude(JsonInclude.Include.NON_EMPTY) 46 public ObjectNode node; 47 ObNodeWrapper()48 protected ObNodeWrapper() { } ObNodeWrapper(ObjectNode n)49 public ObNodeWrapper(ObjectNode n) { 50 node = n; 51 } 52 } 53 54 // [databind#941] 55 static class MyValue 56 { 57 private final ObjectNode object; 58 59 @JsonCreator MyValue(ObjectNode object)60 public MyValue(ObjectNode object) { this.object = object; } 61 62 @JsonValue getObject()63 public ObjectNode getObject() { return object; } 64 } 65 66 /* 67 /********************************************************** 68 /* Test methods 69 /********************************************************** 70 */ 71 72 private final ObjectMapper MAPPER = sharedMapper(); 73 testSimpleObject()74 public void testSimpleObject() throws Exception 75 { 76 String JSON = "{ \"key\" : 1, \"b\" : \"x\" }"; 77 JsonNode root = MAPPER.readTree(JSON); 78 79 // basic properties first: 80 assertFalse(root.isValueNode()); 81 assertTrue(root.isContainerNode()); 82 assertFalse(root.isArray()); 83 assertTrue(root.isObject()); 84 assertEquals(2, root.size()); 85 assertFalse(root.isEmpty()); 86 87 Iterator<JsonNode> it = root.iterator(); 88 assertNotNull(it); 89 assertTrue(it.hasNext()); 90 JsonNode n = it.next(); 91 assertNotNull(n); 92 assertEquals(IntNode.valueOf(1), n); 93 94 assertTrue(it.hasNext()); 95 n = it.next(); 96 assertNotNull(n); 97 assertEquals(TextNode.valueOf("x"), n); 98 99 assertFalse(it.hasNext()); 100 101 // Ok, then, let's traverse via extended interface 102 ObjectNode obNode = (ObjectNode) root; 103 Iterator<Map.Entry<String,JsonNode>> fit = obNode.fields(); 104 // we also know that LinkedHashMap is used, i.e. order preserved 105 assertTrue(fit.hasNext()); 106 Map.Entry<String,JsonNode> en = fit.next(); 107 assertEquals("key", en.getKey()); 108 assertEquals(IntNode.valueOf(1), en.getValue()); 109 110 assertTrue(fit.hasNext()); 111 en = fit.next(); 112 assertEquals("b", en.getKey()); 113 assertEquals(TextNode.valueOf("x"), en.getValue()); 114 115 // Plus: we should be able to modify the node via iterator too: 116 fit.remove(); 117 assertEquals(1, obNode.size()); 118 assertEquals(IntNode.valueOf(1), root.get("key")); 119 assertNull(root.get("b")); 120 } 121 // for [databind#346] testEmptyNodeAsValue()122 public void testEmptyNodeAsValue() throws Exception 123 { 124 Data w = MAPPER.readValue("{}", Data.class); 125 assertNotNull(w); 126 } 127 testBasics()128 public void testBasics() 129 { 130 ObjectNode n = new ObjectNode(JsonNodeFactory.instance); 131 assertStandardEquals(n); 132 assertTrue(n.isEmpty()); 133 134 assertFalse(n.elements().hasNext()); 135 assertFalse(n.fields().hasNext()); 136 assertFalse(n.fieldNames().hasNext()); 137 assertNull(n.get("a")); 138 assertTrue(n.path("a").isMissingNode()); 139 140 TextNode text = TextNode.valueOf("x"); 141 assertSame(n, n.set("a", text)); 142 143 assertEquals(1, n.size()); 144 assertTrue(n.elements().hasNext()); 145 assertTrue(n.fields().hasNext()); 146 assertTrue(n.fieldNames().hasNext()); 147 assertSame(text, n.get("a")); 148 assertSame(text, n.path("a")); 149 assertNull(n.get("b")); 150 assertNull(n.get(0)); // not used with objects 151 152 assertFalse(n.has(0)); 153 assertFalse(n.hasNonNull(0)); 154 assertTrue(n.has("a")); 155 assertTrue(n.hasNonNull("a")); 156 assertFalse(n.has("b")); 157 assertFalse(n.hasNonNull("b")); 158 159 ObjectNode n2 = new ObjectNode(JsonNodeFactory.instance); 160 n2.put("b", 13); 161 assertFalse(n.equals(n2)); 162 n.setAll(n2); 163 164 assertEquals(2, n.size()); 165 n.set("null", (JsonNode)null); 166 assertEquals(3, n.size()); 167 // may be non-intuitive, but explicit nulls do exist in tree: 168 assertTrue(n.has("null")); 169 assertFalse(n.hasNonNull("null")); 170 // should replace, not add 171 n.put("null", "notReallNull"); 172 assertEquals(3, n.size()); 173 assertNotNull(n.remove("null")); 174 assertEquals(2, n.size()); 175 176 Map<String,JsonNode> nodes = new HashMap<String,JsonNode>(); 177 nodes.put("d", text); 178 n.setAll(nodes); 179 assertEquals(3, n.size()); 180 181 n.removeAll(); 182 assertEquals(0, n.size()); 183 } 184 testBigNumbers()185 public void testBigNumbers() 186 { 187 ObjectNode n = new ObjectNode(JsonNodeFactory.instance); 188 assertStandardEquals(n); 189 BigInteger I = BigInteger.valueOf(3); 190 BigDecimal DEC = new BigDecimal("0.1"); 191 192 n.put("a", DEC); 193 n.put("b", I); 194 195 assertEquals(2, n.size()); 196 197 assertTrue(n.path("a").isBigDecimal()); 198 assertEquals(DEC, n.get("a").decimalValue()); 199 assertTrue(n.path("b").isBigInteger()); 200 assertEquals(I, n.get("b").bigIntegerValue()); 201 } 202 203 /** 204 * Verify null handling 205 */ testNullChecking()206 public void testNullChecking() 207 { 208 ObjectNode o1 = JsonNodeFactory.instance.objectNode(); 209 ObjectNode o2 = JsonNodeFactory.instance.objectNode(); 210 // used to throw NPE before fix: 211 o1.setAll(o2); 212 assertEquals(0, o1.size()); 213 assertEquals(0, o2.size()); 214 215 // also: nulls should be converted to NullNodes... 216 o1.set("x", null); 217 JsonNode n = o1.get("x"); 218 assertNotNull(n); 219 assertSame(n, NullNode.instance); 220 221 o1.put("str", (String) null); 222 n = o1.get("str"); 223 assertNotNull(n); 224 assertSame(n, NullNode.instance); 225 226 o1.put("d", (BigDecimal) null); 227 n = o1.get("d"); 228 assertNotNull(n); 229 assertSame(n, NullNode.instance); 230 231 o1.put("3", (BigInteger) null); 232 n = o1.get("3"); 233 assertNotNull(3); 234 assertSame(n, NullNode.instance); 235 236 assertEquals(4, o1.size()); 237 } 238 239 /** 240 * Another test to verify [JACKSON-227]... 241 */ testNullChecking2()242 public void testNullChecking2() 243 { 244 ObjectNode src = MAPPER.createObjectNode(); 245 ObjectNode dest = MAPPER.createObjectNode(); 246 src.put("a", "b"); 247 dest.setAll(src); 248 } 249 testRemove()250 public void testRemove() 251 { 252 ObjectNode ob = MAPPER.createObjectNode(); 253 ob.put("a", "a"); 254 ob.put("b", "b"); 255 ob.put("c", "c"); 256 assertEquals(3, ob.size()); 257 assertSame(ob, ob.without(Arrays.asList("a", "c"))); 258 assertEquals(1, ob.size()); 259 assertEquals("b", ob.get("b").textValue()); 260 } 261 testRetain()262 public void testRetain() 263 { 264 ObjectNode ob = MAPPER.createObjectNode(); 265 ob.put("a", "a"); 266 ob.put("b", "b"); 267 ob.put("c", "c"); 268 assertEquals(3, ob.size()); 269 assertSame(ob, ob.retain("a", "c")); 270 assertEquals(2, ob.size()); 271 assertEquals("a", ob.get("a").textValue()); 272 assertNull(ob.get("b")); 273 assertEquals("c", ob.get("c").textValue()); 274 } 275 testValidWith()276 public void testValidWith() throws Exception 277 { 278 ObjectNode root = MAPPER.createObjectNode(); 279 assertEquals("{}", MAPPER.writeValueAsString(root)); 280 JsonNode child = root.with("prop"); 281 assertTrue(child instanceof ObjectNode); 282 assertEquals("{\"prop\":{}}", MAPPER.writeValueAsString(root)); 283 } 284 testValidWithArray()285 public void testValidWithArray() throws Exception 286 { 287 JsonNode root = MAPPER.createObjectNode(); 288 assertEquals("{}", MAPPER.writeValueAsString(root)); 289 ArrayNode child = root.withArray("arr"); 290 assertTrue(child instanceof ArrayNode); 291 assertEquals("{\"arr\":[]}", MAPPER.writeValueAsString(root)); 292 } 293 testInvalidWith()294 public void testInvalidWith() throws Exception 295 { 296 JsonNode root = MAPPER.createArrayNode(); 297 try { // should not work for non-ObjectNode nodes: 298 root.with("prop"); 299 fail("Expected exception"); 300 } catch (UnsupportedOperationException e) { 301 verifyException(e, "not of type ObjectNode"); 302 } 303 // also: should fail of we already have non-object property 304 ObjectNode root2 = MAPPER.createObjectNode(); 305 root2.put("prop", 13); 306 try { // should not work for non-ObjectNode nodes: 307 root2.with("prop"); 308 fail("Expected exception"); 309 } catch (UnsupportedOperationException e) { 310 verifyException(e, "has value that is not"); 311 } 312 } 313 testInvalidWithArray()314 public void testInvalidWithArray() throws Exception 315 { 316 JsonNode root = MAPPER.createArrayNode(); 317 try { // should not work for non-ObjectNode nodes: 318 root.withArray("prop"); 319 fail("Expected exception"); 320 } catch (UnsupportedOperationException e) { 321 verifyException(e, "not of type ObjectNode"); 322 } 323 // also: should fail of we already have non-Array property 324 ObjectNode root2 = MAPPER.createObjectNode(); 325 root2.put("prop", 13); 326 try { // should not work for non-ObjectNode nodes: 327 root2.withArray("prop"); 328 fail("Expected exception"); 329 } catch (UnsupportedOperationException e) { 330 verifyException(e, "has value that is not"); 331 } 332 } 333 testSetAll()334 public void testSetAll() throws Exception 335 { 336 ObjectNode root = MAPPER.createObjectNode(); 337 assertEquals(0, root.size()); 338 HashMap<String,JsonNode> map = new HashMap<String,JsonNode>(); 339 map.put("a", root.numberNode(1)); 340 root.setAll(map); 341 assertEquals(1, root.size()); 342 assertTrue(root.has("a")); 343 assertFalse(root.has("b")); 344 345 map.put("b", root.numberNode(2)); 346 root.setAll(map); 347 assertEquals(2, root.size()); 348 assertTrue(root.has("a")); 349 assertTrue(root.has("b")); 350 assertEquals(2, root.path("b").intValue()); 351 352 // Then with ObjectNodes... 353 ObjectNode root2 = MAPPER.createObjectNode(); 354 root2.setAll(root); 355 assertEquals(2, root.size()); 356 assertEquals(2, root2.size()); 357 358 root2.setAll(root); 359 assertEquals(2, root.size()); 360 assertEquals(2, root2.size()); 361 362 ObjectNode root3 = MAPPER.createObjectNode(); 363 root3.put("a", 2); 364 root3.put("c", 3); 365 assertEquals(2, root3.path("a").intValue()); 366 root3.setAll(root2); 367 assertEquals(3, root3.size()); 368 assertEquals(1, root3.path("a").intValue()); 369 } 370 371 // [databind#237] (databind): support DeserializationFeature#FAIL_ON_READING_DUP_TREE_KEY testFailOnDupKeys()372 public void testFailOnDupKeys() throws Exception 373 { 374 final String DUP_JSON = "{ \"a\":1, \"a\":2 }"; 375 376 // first: verify defaults: 377 assertFalse(MAPPER.isEnabled(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY)); 378 ObjectNode root = (ObjectNode) MAPPER.readTree(DUP_JSON); 379 assertEquals(2, root.path("a").asInt()); 380 381 // and then enable checks: 382 try { 383 MAPPER.reader(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY).readTree(DUP_JSON); 384 fail("Should have thrown exception!"); 385 } catch (JsonMappingException e) { 386 verifyException(e, "duplicate field 'a'"); 387 } 388 } 389 testFailOnDupNestedKeys()390 public void testFailOnDupNestedKeys() throws Exception 391 { 392 final String DOC = aposToQuotes( 393 "{'node' : { 'data' : [ 1, 2, { 'a':3 }, { 'foo' : 1, 'bar' : 2, 'foo': 3}]}}" 394 ); 395 try { 396 MAPPER.readerFor(ObNodeWrapper.class) 397 .with(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY) 398 .readValue(DOC); 399 fail("Should have thrown exception!"); 400 } catch (JsonMappingException e) { 401 verifyException(e, "duplicate field 'foo'"); 402 } 403 } 404 testEqualityWrtOrder()405 public void testEqualityWrtOrder() throws Exception 406 { 407 ObjectNode ob1 = MAPPER.createObjectNode(); 408 ObjectNode ob2 = MAPPER.createObjectNode(); 409 410 // same contents, different insertion order; should not matter 411 412 ob1.put("a", 1); 413 ob1.put("b", 2); 414 ob1.put("c", 3); 415 416 ob2.put("b", 2); 417 ob2.put("c", 3); 418 ob2.put("a", 1); 419 420 assertTrue(ob1.equals(ob2)); 421 assertTrue(ob2.equals(ob1)); 422 } 423 testSimplePath()424 public void testSimplePath() throws Exception 425 { 426 JsonNode root = MAPPER.readTree("{ \"results\" : { \"a\" : 3 } }"); 427 assertTrue(root.isObject()); 428 JsonNode rnode = root.path("results"); 429 assertNotNull(rnode); 430 assertTrue(rnode.isObject()); 431 assertEquals(3, rnode.path("a").intValue()); 432 } 433 testNonEmptySerialization()434 public void testNonEmptySerialization() throws Exception 435 { 436 ObNodeWrapper w = new ObNodeWrapper(MAPPER.createObjectNode() 437 .put("a", 3)); 438 assertEquals("{\"node\":{\"a\":3}}", MAPPER.writeValueAsString(w)); 439 w = new ObNodeWrapper(MAPPER.createObjectNode()); 440 assertEquals("{}", MAPPER.writeValueAsString(w)); 441 } 442 testIssue941()443 public void testIssue941() throws Exception 444 { 445 ObjectNode object = MAPPER.createObjectNode(); 446 447 String json = MAPPER.writeValueAsString(object); 448 // System.out.println("json: "+json); 449 450 ObjectNode de1 = MAPPER.readValue(json, ObjectNode.class); // this works 451 // System.out.println("Deserialized to ObjectNode: "+de1); 452 assertNotNull(de1); 453 454 MyValue de2 = MAPPER.readValue(json, MyValue.class); // but this throws exception 455 // System.out.println("Deserialized to MyValue: "+de2); 456 assertNotNull(de2); 457 } 458 testSimpleMismatch()459 public void testSimpleMismatch() throws Exception 460 { 461 ObjectMapper mapper = objectMapper(); 462 try { 463 mapper.readValue("[ 1, 2, 3 ]", ObjectNode.class); 464 fail("Should not pass"); 465 } catch (MismatchedInputException e) { 466 verifyException(e, "from Array value (token `JsonToken.START_ARRAY`)"); 467 } 468 } 469 } 470