| package com.fasterxml.jackson.databind.deser.merge; |
| |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| import com.fasterxml.jackson.annotation.*; |
| import com.fasterxml.jackson.annotation.JsonFormat.Shape; |
| |
| import com.fasterxml.jackson.databind.*; |
| import com.fasterxml.jackson.databind.exc.MismatchedInputException; |
| import com.fasterxml.jackson.databind.exc.InvalidDefinitionException; |
| |
| /** |
| * Tests to make sure that the new "merging" property of |
| * <code>JsonSetter</code> annotation works as expected. |
| * |
| * @since 2.9 |
| */ |
| @SuppressWarnings("serial") |
| public class PropertyMergeTest extends BaseMapTest |
| { |
| static class Config { |
| @JsonMerge |
| public AB loc = new AB(1, 2); |
| |
| protected Config() { } |
| public Config(int a, int b) { |
| loc = new AB(a, b); |
| } |
| } |
| |
| static class NonMergeConfig { |
| public AB loc = new AB(1, 2); |
| } |
| |
| // another variant where all we got is a getter |
| static class NoSetterConfig { |
| AB _value = new AB(1, 2); |
| |
| @JsonMerge |
| public AB getValue() { return _value; } |
| } |
| |
| static class AB { |
| public int a; |
| public int b; |
| |
| protected AB() { } |
| public AB(int a0, int b0) { |
| a = a0; |
| b = b0; |
| } |
| } |
| |
| @JsonPropertyOrder(alphabetic=true) |
| @JsonFormat(shape=Shape.ARRAY) |
| static class ABAsArray { |
| public int a; |
| public int b; |
| } |
| |
| // Custom type that would be deserializable by default |
| static class StringReference extends AtomicReference<String> { |
| public StringReference(String str) { |
| set(str); |
| } |
| } |
| |
| static class MergedReference |
| { |
| @JsonMerge |
| public StringReference value = new StringReference("default"); |
| } |
| |
| static class MergedX<T> |
| { |
| @JsonMerge |
| public T value; |
| |
| public MergedX(T v) { value = v; } |
| protected MergedX() { } |
| } |
| |
| // // // Classes with invalid merge definition(s) |
| |
| static class CantMergeInts { |
| @JsonMerge |
| public int value; |
| } |
| |
| /* |
| /******************************************************** |
| /* Test methods, POJO merging |
| /******************************************************** |
| */ |
| |
| private final ObjectMapper MAPPER = newObjectMapper() |
| // 26-Oct-2016, tatu: Make sure we'll report merge problems by default |
| .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE) |
| ; |
| |
| public void testBeanMergingViaProp() throws Exception |
| { |
| Config config = MAPPER.readValue(aposToQuotes("{'loc':{'b':3}}"), Config.class); |
| assertEquals(1, config.loc.a); |
| assertEquals(3, config.loc.b); |
| |
| config = MAPPER.readerForUpdating(new Config(5, 7)) |
| .readValue(aposToQuotes("{'loc':{'b':2}}")); |
| assertEquals(5, config.loc.a); |
| assertEquals(2, config.loc.b); |
| } |
| |
| public void testBeanMergingViaType() throws Exception |
| { |
| // by default, no merging |
| NonMergeConfig config = MAPPER.readValue(aposToQuotes("{'loc':{'a':3}}"), NonMergeConfig.class); |
| assertEquals(3, config.loc.a); |
| assertEquals(0, config.loc.b); // not passed, nor merge from original |
| |
| // but with type-overrides |
| ObjectMapper mapper = newObjectMapper(); |
| mapper.configOverride(AB.class).setMergeable(true); |
| config = mapper.readValue(aposToQuotes("{'loc':{'a':3}}"), NonMergeConfig.class); |
| assertEquals(3, config.loc.a); |
| assertEquals(2, config.loc.b); // original, merged |
| } |
| |
| public void testBeanMergingViaGlobal() throws Exception |
| { |
| // but with type-overrides |
| ObjectMapper mapper = newObjectMapper() |
| .setDefaultMergeable(true); |
| NonMergeConfig config = mapper.readValue(aposToQuotes("{'loc':{'a':3}}"), NonMergeConfig.class); |
| assertEquals(3, config.loc.a); |
| assertEquals(2, config.loc.b); // original, merged |
| |
| // also, test with bigger POJO type; just as smoke test |
| FiveMinuteUser user0 = new FiveMinuteUser("Bob", "Bush", true, FiveMinuteUser.Gender.MALE, |
| new byte[] { 1, 2, 3, 4, 5 }); |
| FiveMinuteUser user = mapper.readerFor(FiveMinuteUser.class) |
| .withValueToUpdate(user0) |
| .readValue(aposToQuotes("{'name':{'last':'Brown'}}")); |
| assertEquals("Bob", user.getName().getFirst()); |
| assertEquals("Brown", user.getName().getLast()); |
| } |
| |
| // should even work with no setter |
| public void testBeanMergingWithoutSetter() throws Exception |
| { |
| NoSetterConfig config = MAPPER.readValue(aposToQuotes("{'value':{'b':99}}"), |
| NoSetterConfig.class); |
| assertEquals(99, config._value.b); |
| assertEquals(1, config._value.a); |
| } |
| |
| /* |
| /******************************************************** |
| /* Test methods, as array |
| /******************************************************** |
| */ |
| |
| public void testBeanAsArrayMerging() throws Exception |
| { |
| ABAsArray input = new ABAsArray(); |
| input.a = 4; |
| input.b = 6; |
| |
| assertSame(input, MAPPER.readerForUpdating(input) |
| .readValue("[1, 3]")); |
| assertEquals(1, input.a); |
| assertEquals(3, input.b); |
| |
| // then with one too few |
| assertSame(input, MAPPER.readerForUpdating(input) |
| .readValue("[9]")); |
| assertEquals(9, input.a); |
| assertEquals(3, input.b); |
| |
| // and finally with extra, failing |
| try { |
| MAPPER.readerForUpdating(input) |
| .readValue("[9, 8, 14]"); |
| fail("Should not pass"); |
| } catch (MismatchedInputException e) { |
| verifyException(e, "expected at most 2 properties"); |
| } |
| |
| try { |
| MAPPER.readerForUpdating(input) |
| .readValue("\"blob\""); |
| fail("Should not pass"); |
| } catch (MismatchedInputException e) { |
| verifyException(e, "Cannot deserialize"); |
| verifyException(e, "from non-Array representation"); |
| } |
| } |
| |
| /* |
| /******************************************************** |
| /* Test methods, reference types |
| /******************************************************** |
| */ |
| |
| public void testReferenceMerging() throws Exception |
| { |
| MergedReference result = MAPPER.readValue(aposToQuotes("{'value':'override'}"), |
| MergedReference.class); |
| assertEquals("override", result.value.get()); |
| } |
| |
| /* |
| /******************************************************** |
| /* Test methods, failure checking |
| /******************************************************** |
| */ |
| |
| public void testInvalidPropertyMerge() throws Exception |
| { |
| ObjectMapper mapper = newObjectMapper() |
| .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE); |
| |
| try { |
| mapper.readValue("{\"value\":3}", CantMergeInts.class); |
| fail("Should not pass"); |
| } catch (InvalidDefinitionException e) { |
| verifyException(e, "cannot be merged"); |
| } |
| } |
| } |