aboutsummaryrefslogtreecommitdiff
path: root/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/TestPropertyCreatorSubtypesExternalPropertyMissingProperty.java
blob: f3bb4449851383a720f35a1c81493e939ce90f9c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
package com.fasterxml.jackson.databind.jsontype.ext;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;

// for [databind#2404]
public class TestPropertyCreatorSubtypesExternalPropertyMissingProperty
{
    /**
     * Base class - external property for Fruit subclasses.
     */
    static class Box {
        private String type;
        @JsonTypeInfo(use = Id.NAME, include = As.EXTERNAL_PROPERTY, property = "type")
        @JsonSubTypes({
                @Type(value = Apple.class, name = "apple"),
                @Type(value = Orange.class, name = "orange")
        })
        private Fruit fruit;

        private Box(String type, Fruit fruit) {
            this.type = type;
            this.fruit = fruit;
        }

        @JsonCreator
        public static Box getBox(@JsonProperty("type") String type, @JsonProperty("fruit") Fruit fruit) {
            return new Box(type, fruit);
        }

        public String getType() {
            return type;
        }

        public Fruit getFruit() {
            return fruit;
        }
    }

    static abstract class Fruit {
        private String name;

        protected Fruit(String n) {
            name = n;
        }

        public String getName() {
            return name;
        }
    }

    static class Apple extends Fruit {
        private int seedCount;

        private Apple(String name, int b) {
            super(name);
            seedCount = b;
        }

        public int getSeedCount() {
            return seedCount;
        }

        @JsonCreator
        public static Apple getApple(@JsonProperty("name") String name, @JsonProperty("seedCount") int seedCount) {
            return new Apple(name, seedCount);
        }
    }

    static class Orange extends Fruit {
        private String color;
        private Orange(String name, String c) {
            super(name);
            color = c;
        }

        public String getColor() {
            return color;
        }

        @JsonCreator
        public static Orange getOrange(@JsonProperty("name") String name, @JsonProperty("color") String color) {
            return new Orange(name, color);
        }
    }

    /*
    /**********************************************************
    /* Mock data
    /**********************************************************
     */

    private static final Orange orange = new Orange("Orange", "orange");
    private static final Box orangeBox = new Box("orange", orange);
    private static final String orangeBoxJson = "{\"type\":\"orange\",\"fruit\":{\"name\":\"Orange\",\"color\":\"orange\"}}";
    private static final String orangeBoxNullJson = "{\"type\":\"orange\",\"fruit\":null}}";
    private static final String orangeBoxEmptyJson = "{\"type\":\"orange\",\"fruit\":{}}}";
    private static final String orangeBoxMissingJson = "{\"type\":\"orange\"}}";

    private static final Apple apple = new Apple("Apple", 16);
    private static Box appleBox = new Box("apple", apple);
    private static final String appleBoxJson = "{\"type\":\"apple\",\"fruit\":{\"name\":\"Apple\",\"seedCount\":16}}";
    private static final String appleBoxNullJson = "{\"type\":\"apple\",\"fruit\":null}";
    private static final String appleBoxEmptyJson = "{\"type\":\"apple\",\"fruit\":{}}";
    private static final String appleBoxMissingJson = "{\"type\":\"apple\"}";

    /*
    /**********************************************************
    /* Unit tests
    /**********************************************************
     */

    private final ObjectReader BOX_READER_PASS;
    private final ObjectReader BOX_READER_FAIL;

    {
        final ObjectMapper mapper = new ObjectMapper();
        BOX_READER_PASS = mapper.readerFor(Box.class)
            .without(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY);
        BOX_READER_FAIL = mapper.readerFor(Box.class)
            .with(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY);
    }

    /**
     * Deserialization tests for external type id property present
     */
    @Test
    public void testDeserializationPresent() throws Exception {
        checkOrangeBox(BOX_READER_PASS);
        checkAppleBox(BOX_READER_PASS);

        checkOrangeBox(BOX_READER_FAIL);
        checkAppleBox(BOX_READER_FAIL);
    }

    /**
     * Deserialization tests for external type id property null
     */
    @Test
    public void testDeserializationNull() throws Exception {
        checkOrangeBoxNull(BOX_READER_PASS, orangeBoxNullJson);
        checkAppleBoxNull(BOX_READER_PASS, appleBoxNullJson);

        checkOrangeBoxNull(BOX_READER_FAIL, orangeBoxNullJson);
        checkAppleBoxNull(BOX_READER_FAIL, appleBoxNullJson);
    }

    /**
     * Deserialization tests for external type id property empty
     */
    @Test
    public void testDeserializationEmpty() throws Exception {
        checkOrangeBoxEmpty(BOX_READER_PASS, orangeBoxEmptyJson);
        checkAppleBoxEmpty(BOX_READER_PASS, appleBoxEmptyJson);

        checkOrangeBoxEmpty(BOX_READER_FAIL, orangeBoxEmptyJson);
        checkAppleBoxEmpty(BOX_READER_FAIL, appleBoxEmptyJson);
    }

    /**
     * Deserialization tests for external type id property missing
     */
    @Test
    public void testDeserializationMissing() throws Exception {
        checkOrangeBoxNull(BOX_READER_PASS, orangeBoxMissingJson);
        checkAppleBoxNull(BOX_READER_PASS, appleBoxMissingJson);

        checkBoxJsonMappingException(BOX_READER_FAIL, orangeBoxMissingJson);
        checkBoxJsonMappingException(BOX_READER_FAIL, appleBoxMissingJson);
    }

    private void checkOrangeBox(ObjectReader reader) throws Exception {
        Box deserOrangeBox = reader.readValue(orangeBoxJson);
        assertEquals(orangeBox.getType(), deserOrangeBox.getType());

        Fruit deserOrange = deserOrangeBox.getFruit();
        assertSame(Orange.class, deserOrange.getClass());
        assertEquals(orange.getName(), deserOrange.getName());
        assertEquals(orange.getColor(), ((Orange) deserOrange).getColor());
    }

    private void checkAppleBox(ObjectReader reader) throws Exception {
        Box deserAppleBox = reader.readValue(appleBoxJson);
        assertEquals(appleBox.getType(), deserAppleBox.getType());

        Fruit deserApple = deserAppleBox.fruit;
        assertSame(Apple.class, deserApple.getClass());
        assertEquals(apple.getName(), deserApple.getName());
        assertEquals(apple.getSeedCount(), ((Apple) deserApple).getSeedCount());
    }

    private void checkOrangeBoxEmpty(ObjectReader reader, String json) throws Exception {
        Box deserOrangeBox = reader.readValue(json);
        assertEquals(orangeBox.getType(), deserOrangeBox.getType());

        Fruit deserOrange = deserOrangeBox.getFruit();
        assertSame(Orange.class, deserOrange.getClass());
        assertNull(deserOrange.getName());
        assertNull(((Orange) deserOrange).getColor());
    }

    private void checkAppleBoxEmpty(ObjectReader reader, String json) throws Exception {
        Box deserAppleBox = reader.readValue(json);
        assertEquals(appleBox.getType(), deserAppleBox.getType());

        Fruit deserApple = deserAppleBox.fruit;
        assertSame(Apple.class, deserApple.getClass());
        assertNull(deserApple.getName());
        assertEquals(0, ((Apple) deserApple).getSeedCount());
    }

    private void checkOrangeBoxNull(ObjectReader reader, String json) throws Exception {
        Box deserOrangeBox = reader.readValue(json);
        assertEquals(orangeBox.getType(), deserOrangeBox.getType());
        assertNull(deserOrangeBox.getFruit());
    }

    private void checkAppleBoxNull(ObjectReader reader, String json) throws Exception {
        Box deserAppleBox = reader.readValue(json);
        assertEquals(appleBox.getType(), deserAppleBox.getType());
        assertNull(deserAppleBox.getFruit());
    }

    private void checkBoxJsonMappingException(ObjectReader reader, String json) throws Exception {
        try {
            reader.readValue(json);
            fail("Should not pass");
        } catch (MismatchedInputException e) {
            BaseMapTest.verifyException(e, "Missing property 'fruit' for external type id 'type'");
        }
    }
}