package com.fasterxml.jackson.databind.convert; import java.io.IOException; import java.util.*; import com.fasterxml.jackson.annotation.JsonView; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; import static org.junit.Assert.assertArrayEquals; /** * Unit tests for verifying that "updating reader" works as * expected. */ public class TestUpdateViaObjectReader extends BaseMapTest { static class Bean { public String a = "a"; public String b = "b"; public int[] c = new int[] { 1, 2, 3 }; public Bean child = null; } static class XYBean { public int x, y; } public class TextView {} public class NumView {} public class Updateable { @JsonView(NumView.class) public int num; @JsonView(TextView.class) public String str; } // for [databind#744] static class DataA { public int i = 1; public int j = 2; } static class DataB { public DataA da = new DataA(); public int k = 3; } static class DataADeserializer extends StdDeserializer<DataA> { private static final long serialVersionUID = 1L; DataADeserializer() { super(DataA.class); } @Override public DataA deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { if (p.getCurrentToken() != JsonToken.START_OBJECT) { ctxt.reportInputMismatch(DataA.class, "Wrong current token, expected START_OBJECT, got: " +p.getCurrentToken()); // never gets here } /*JsonNode node =*/ p.readValueAsTree(); DataA da = new DataA(); da.i = 5; return da; } } /* /******************************************************** /* Test methods /******************************************************** */ private final ObjectMapper MAPPER = new ObjectMapper(); public void testBeanUpdate() throws Exception { Bean bean = new Bean(); assertEquals("b", bean.b); assertEquals(3, bean.c.length); assertNull(bean.child); Object ob = MAPPER.readerForUpdating(bean).readValue("{ \"b\":\"x\", \"c\":[4,5], \"child\":{ \"a\":\"y\"} }"); assertSame(ob, bean); assertEquals("a", bean.a); assertEquals("x", bean.b); assertArrayEquals(new int[] { 4, 5 }, bean.c); Bean child = bean.child; assertNotNull(child); assertEquals("y", child.a); assertEquals("b", child.b); assertArrayEquals(new int[] { 1, 2, 3 }, child.c); assertNull(child.child); } public void testListUpdate() throws Exception { List<String> strs = new ArrayList<String>(); strs.add("a"); // for lists, we will be appending entries Object ob = MAPPER.readerForUpdating(strs).readValue("[ \"b\", \"c\", \"d\" ]"); assertSame(strs, ob); assertEquals(4, strs.size()); assertEquals("a", strs.get(0)); assertEquals("b", strs.get(1)); assertEquals("c", strs.get(2)); assertEquals("d", strs.get(3)); } public void testMapUpdate() throws Exception { Map<String,String> strs = new HashMap<String,String>(); strs.put("a", "a"); strs.put("b", "b"); // for maps, we will be adding and/or overwriting entries Object ob = MAPPER.readerForUpdating(strs).readValue("{ \"c\" : \"c\", \"a\" : \"z\" }"); assertSame(strs, ob); assertEquals(3, strs.size()); assertEquals("z", strs.get("a")); assertEquals("b", strs.get("b")); assertEquals("c", strs.get("c")); } // Test for [JACKSON-717] -- ensure 'readValues' also does update @SuppressWarnings("resource") public void testUpdateSequence() throws Exception { XYBean toUpdate = new XYBean(); Iterator<XYBean> it = MAPPER.readerForUpdating(toUpdate).readValues( "{\"x\":1,\"y\":2}\n{\"x\":16}{\"y\":37}"); assertTrue(it.hasNext()); XYBean value = it.next(); assertSame(toUpdate, value); assertEquals(1, value.x); assertEquals(2, value.y); assertTrue(it.hasNext()); value = it.next(); assertSame(toUpdate, value); assertEquals(16, value.x); assertEquals(2, value.y); // unchanged assertTrue(it.hasNext()); value = it.next(); assertSame(toUpdate, value); assertEquals(16, value.x); // unchanged assertEquals(37, value.y); assertFalse(it.hasNext()); } // [JACKSON-824] public void testUpdatingWithViews() throws Exception { Updateable bean = new Updateable(); bean.num = 100; bean.str = "test"; Updateable result = MAPPER.readerForUpdating(bean) .withView(TextView.class) .readValue("{\"num\": 10, \"str\":\"foobar\"}"); assertSame(bean, result); assertEquals(100, bean.num); assertEquals("foobar", bean.str); } // [databind#744] public void testIssue744() throws IOException { ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(DataA.class, new DataADeserializer()); mapper.registerModule(module); DataB db = new DataB(); db.da.i = 11; db.k = 13; String jsonBString = mapper.writeValueAsString(db); JsonNode jsonBNode = mapper.valueToTree(db); // create parent DataB dbNewViaString = mapper.readValue(jsonBString, DataB.class); assertEquals(5, dbNewViaString.da.i); assertEquals(13, dbNewViaString.k); DataB dbNewViaNode = mapper.treeToValue(jsonBNode, DataB.class); assertEquals(5, dbNewViaNode.da.i); assertEquals(13, dbNewViaNode.k); // update parent DataB dbUpdViaString = new DataB(); DataB dbUpdViaNode = new DataB(); assertEquals(1, dbUpdViaString.da.i); assertEquals(3, dbUpdViaString.k); mapper.readerForUpdating(dbUpdViaString).readValue(jsonBString); assertEquals(5, dbUpdViaString.da.i); assertEquals(13, dbUpdViaString.k); assertEquals(1, dbUpdViaNode.da.i); assertEquals(3, dbUpdViaNode.k); mapper.readerForUpdating(dbUpdViaNode).readValue(jsonBNode); assertEquals(5, dbUpdViaNode.da.i); assertEquals(13, dbUpdViaNode.k); } }