1 package com.fasterxml.jackson.databind.struct; 2 3 import com.fasterxml.jackson.annotation.*; 4 5 import com.fasterxml.jackson.databind.BaseMapTest; 6 import com.fasterxml.jackson.databind.DeserializationFeature; 7 import com.fasterxml.jackson.databind.ObjectMapper; 8 import com.fasterxml.jackson.databind.ObjectReader; 9 import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 10 import com.fasterxml.jackson.databind.exc.MismatchedInputException; 11 import com.fasterxml.jackson.databind.exc.InvalidDefinitionException; 12 13 /** 14 * Unit tests for "POJO as array" feature using Builder-style 15 * POJO construction. 16 */ 17 public class TestPOJOAsArrayWithBuilder extends BaseMapTest 18 { 19 @JsonDeserialize(builder=SimpleBuilderXY.class) 20 @JsonFormat(shape=JsonFormat.Shape.ARRAY) 21 @JsonPropertyOrder(alphabetic=true) 22 static class ValueClassXY 23 { 24 final int _x, _y; 25 ValueClassXY(int x, int y)26 protected ValueClassXY(int x, int y) { 27 _x = x+1; 28 _y = y+1; 29 } 30 } 31 32 @JsonFormat(shape=JsonFormat.Shape.ARRAY) 33 static class SimpleBuilderXY 34 { 35 public int x, y; 36 SimpleBuilderXY()37 protected SimpleBuilderXY() { } SimpleBuilderXY(int x0, int y0)38 protected SimpleBuilderXY(int x0, int y0) { 39 x = x0; 40 y = y0; 41 } 42 withX(int x0)43 public SimpleBuilderXY withX(int x0) { 44 this.x = x0; 45 return this; 46 } 47 withY(int y0)48 public SimpleBuilderXY withY(int y0) { 49 this.y = y0; 50 return this; 51 } 52 build()53 public ValueClassXY build() { 54 return new ValueClassXY(x, y); 55 } 56 } 57 58 // Also, with creator: 59 60 @JsonDeserialize(builder=CreatorBuilder.class) 61 @JsonFormat(shape=JsonFormat.Shape.ARRAY) 62 @JsonPropertyOrder(alphabetic=true) 63 static class CreatorValue 64 { 65 final int a, b, c; 66 CreatorValue(int a, int b, int c)67 protected CreatorValue(int a, int b, int c) { 68 this.a = a; 69 this.b = b; 70 this.c = c; 71 } 72 } 73 74 @JsonFormat(shape=JsonFormat.Shape.ARRAY) 75 static class CreatorBuilder { 76 private final int a, b; 77 78 private int c; 79 80 @JsonCreator CreatorBuilder(@sonProperty"a") int a, @JsonProperty("b") int b)81 public CreatorBuilder(@JsonProperty("a") int a, 82 @JsonProperty("b") int b) 83 { 84 this.a = a; 85 this.b = b; 86 } 87 88 @JsonView(String.class) withC(int v)89 public CreatorBuilder withC(int v) { 90 c = v; 91 return this; 92 } 93 build()94 public CreatorValue build() { 95 return new CreatorValue(a, b, c); 96 } 97 } 98 99 /* 100 /***************************************************** 101 /* Basic tests 102 /***************************************************** 103 */ 104 105 private final static ObjectMapper MAPPER = new ObjectMapper(); 106 testSimpleBuilder()107 public void testSimpleBuilder() throws Exception 108 { 109 // Ok, first, ensure that serializer will "black out" filtered properties 110 ValueClassXY value = MAPPER.readValue("[1,2]", ValueClassXY.class); 111 assertEquals(2, value._x); 112 assertEquals(3, value._y); 113 } 114 115 // Won't work, but verify exception testBuilderWithUpdate()116 public void testBuilderWithUpdate() throws Exception 117 { 118 // Ok, first, simple case of all values being present 119 try { 120 /*value =*/ MAPPER.readerFor(ValueClassXY.class) 121 .withValueToUpdate(new ValueClassXY(6, 7)) 122 .readValue("[1,2]"); 123 fail("Should not pass"); 124 } catch (InvalidDefinitionException e) { 125 verifyException(e, "Deserialization of"); 126 verifyException(e, "by passing existing instance"); 127 verifyException(e, "ValueClassXY"); 128 } 129 } 130 131 /* 132 /***************************************************** 133 /* Creator test(s) 134 /***************************************************** 135 */ 136 137 // test to ensure @JsonCreator also works testWithCreator()138 public void testWithCreator() throws Exception 139 { 140 CreatorValue value = MAPPER.readValue("[1,2,3]", CreatorValue.class); 141 assertEquals(1, value.a); 142 assertEquals(2, value.b); 143 assertEquals(3, value.c); 144 145 // and should be ok with partial too? 146 value = MAPPER.readValue("[1,2]", CreatorValue.class); 147 assertEquals(1, value.a); 148 assertEquals(2, value.b); 149 assertEquals(0, value.c); 150 151 value = MAPPER.readValue("[1]", CreatorValue.class); 152 assertEquals(1, value.a); 153 assertEquals(0, value.b); 154 assertEquals(0, value.c); 155 156 value = MAPPER.readValue("[]", CreatorValue.class); 157 assertEquals(0, value.a); 158 assertEquals(0, value.b); 159 assertEquals(0, value.c); 160 } 161 testWithCreatorAndView()162 public void testWithCreatorAndView() throws Exception 163 { 164 ObjectReader reader = MAPPER.readerFor(CreatorValue.class); 165 CreatorValue value; 166 167 // First including values in view 168 value = reader.withView(String.class).readValue("[1,2,3]"); 169 assertEquals(1, value.a); 170 assertEquals(2, value.b); 171 assertEquals(3, value.c); 172 173 // then not including view 174 value = reader.withView(Character.class).readValue("[1,2,3]"); 175 assertEquals(1, value.a); 176 assertEquals(2, value.b); 177 assertEquals(0, value.c); 178 } 179 180 /* 181 /***************************************************** 182 /* Failure tests 183 /***************************************************** 184 */ 185 testUnknownExtraProp()186 public void testUnknownExtraProp() throws Exception 187 { 188 String json = "[1, 2, 3, 4]"; 189 try { 190 MAPPER.readValue(json, ValueClassXY.class); 191 fail("should not pass with extra element"); 192 } catch (MismatchedInputException e) { 193 verifyException(e, "Unexpected JSON values"); 194 } 195 196 // but actually fine if skip-unknown set 197 ValueClassXY v = MAPPER.readerFor(ValueClassXY.class) 198 .without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) 199 .readValue(json); 200 assertNotNull(v); 201 // note: +1 for both so 202 assertEquals(v._x, 2); 203 assertEquals(v._y, 3); 204 } 205 } 206