View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.builder;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertThrows;
21  
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collections;
25  import java.util.Date;
26  import java.util.HashMap;
27  import java.util.LinkedHashMap;
28  import java.util.List;
29  import java.util.Map;
30  
31  import org.apache.commons.lang3.AbstractLangTest;
32  import org.apache.commons.lang3.builder.ToStringStyleTest.Person;
33  import org.junit.jupiter.api.AfterEach;
34  import org.junit.jupiter.api.BeforeEach;
35  import org.junit.jupiter.api.Test;
36  
37  /**
38   * Tests {@link org.apache.commons.lang3.builder.JsonToStringStyleTest}.
39   */
40  class JsonToStringStyleTest extends AbstractLangTest {
41  
42      static class AcademyClass {
43          Teacher teacher;
44          List<Student> students;
45  
46          public List<Student> getStudents() {
47              return students;
48          }
49  
50          public Teacher getTeacher() {
51              return teacher;
52          }
53  
54          public void setStudents(final List<Student> students) {
55              this.students = students;
56          }
57  
58          public void setTeacher(final Teacher teacher) {
59              this.teacher = teacher;
60          }
61  
62          @Override
63          public String toString() {
64              return ToStringBuilder.reflectionToString(this);
65          }
66      }
67  
68      enum EmptyEnum {
69      }
70  
71      enum Hobby {
72          SPORT,
73          BOOK,
74          MUSIC
75      }
76  
77      /**
78       * An object with a Map field used to test {@code ToStringStyle.JsonToStringStyle}.
79       */
80      static class InnerMapObject {
81          /**
82           * Test String field.
83           */
84          String pid;
85  
86          /**
87           * Test inner map field.
88           */
89          Map<String, Object> map;
90      }
91  
92      /**
93       * An object with nested object structures used to test {@code ToStringStyle.JsonToStringStyle}.
94       */
95      static class NestingPerson {
96          /**
97           * Test String field.
98           */
99          String pid;
100 
101         /**
102          * Test nested object field.
103          */
104         Person person;
105     }
106 
107     static class Student {
108         List<Hobby> hobbies;
109 
110         public List<Hobby> getHobbies() {
111             return hobbies;
112         }
113 
114         public void setHobbies(final List<Hobby> hobbies) {
115             this.hobbies = hobbies;
116         }
117 
118         @Override
119         public String toString() {
120             return ToStringBuilder.reflectionToString(this);
121         }
122     }
123 
124     static class Teacher {
125         Hobby[] hobbies;
126 
127         public Hobby[] getHobbies() {
128             return hobbies;
129         }
130 
131         public void setHobbies(final Hobby[] hobbies) {
132             this.hobbies = hobbies;
133         }
134 
135         @Override
136         public String toString() {
137             return ToStringBuilder.reflectionToString(this);
138         }
139     }
140 
141     private final Integer base = Integer.valueOf(5);
142 
143     @BeforeEach
144     public void setUp() {
145         ToStringBuilder.setDefaultStyle(ToStringStyle.JSON_STYLE);
146     }
147 
148     @AfterEach
149     public void tearDown() {
150         ToStringBuilder.setDefaultStyle(ToStringStyle.DEFAULT_STYLE);
151     }
152 
153     @Test
154     void testAppendSuper() {
155         assertEquals(
156                 "{}",
157                 new ToStringBuilder(base).appendSuper(
158                         "Integer@8888[" + System.lineSeparator() + "]")
159                         .toString());
160         assertEquals(
161                 "{}",
162                 new ToStringBuilder(base).appendSuper(
163                         "Integer@8888[" + System.lineSeparator() + "  null"
164                                 + System.lineSeparator() + "]").toString());
165         assertEquals(
166                 "{\"a\":\"hello\"}",
167                 new ToStringBuilder(base)
168                         .appendSuper(
169                                 "Integer@8888[" + System.lineSeparator()
170                                         + "]").append("a", "hello").toString());
171         assertEquals(
172                 "{\"a\":\"hello\"}",
173                 new ToStringBuilder(base)
174                         .appendSuper(
175                                 "Integer@8888[" + System.lineSeparator()
176                                         + "  null" + System.lineSeparator()
177                                         + "]").append("a", "hello").toString());
178         assertEquals("{\"a\":\"hello\"}", new ToStringBuilder(base)
179                 .appendSuper(null).append("a", "hello").toString());
180 
181         assertEquals("{\"a\":\"hello\",\"b\":\"world\"}", new ToStringBuilder(base)
182                 .appendSuper("{\"a\":\"hello\"}").append("b", "world").toString());
183     }
184 
185     @Test
186     void testArray() {
187         final Person p = new Person();
188         p.name = "Jane Doe";
189         p.age = 25;
190         p.smoker = true;
191 
192         assertEquals(
193                 "{\"name\":\"Jane Doe\",\"age\":25,\"smoker\":true,\"groups\":['admin', 'manager', 'user']}",
194                 new ToStringBuilder(p).append("name", p.name)
195                         .append("age", p.age).append("smoker", p.smoker)
196                         .append("groups", new Object() {
197                             @Override
198                             public String toString() {
199                                 return "['admin', 'manager', 'user']";
200                             }
201                         })
202                         .toString());
203     }
204 
205     @Test
206     void testArrayEnum() {
207         final Teacher teacher = new Teacher();
208         final Hobby[] hobbies = new Hobby[3];
209         hobbies[0] = Hobby.BOOK;
210         hobbies[1] = Hobby.SPORT;
211         hobbies[2] = Hobby.MUSIC;
212 
213         teacher.setHobbies(hobbies);
214 
215         assertEquals(teacher.toString(), "{\"hobbies\":[\"BOOK\",\"SPORT\",\"MUSIC\"]}");
216         teacher.setHobbies(new Hobby[0]);
217         assertEquals(teacher.toString(), "{\"hobbies\":[]}");
218         teacher.setHobbies(null);
219         assertEquals(teacher.toString(), "{\"hobbies\":null}");
220     }
221 
222     @Test
223     void testBlank() {
224         assertEquals("{}", new ToStringBuilder(base).toString());
225     }
226 
227     @Test
228     void testBooleanArray() {
229         final boolean[] array = { true, false };
230         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
231         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
232         assertEquals("{\"booleanArray\":[true,false]}", toStringBuilder.append("booleanArray", array).toString());
233         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
234         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
235         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
236     }
237 
238     @Test
239     void testByteArray() {
240         final byte[] array = { 1, 2, -3, 4 };
241         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
242         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
243         assertEquals("{\"byteArray\":[1,2,-3,4]}", toStringBuilder.append("byteArray", array).toString());
244         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
245         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
246         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
247     }
248 
249     @Test
250     void testChar() {
251         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append('A').toString());
252         assertEquals("{\"a\":\"A\"}", new ToStringBuilder(base).append("a", 'A').toString());
253         assertEquals("{\"a\":\"A\",\"b\":\"B\"}", new ToStringBuilder(base).append("a", 'A').append("b", 'B').toString());
254     }
255 
256     @Test
257     void testCharArray() {
258         final char[] array = { '1', '2', '3', '4' };
259         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
260         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
261         assertEquals("{\"charArray\":[\"1\",\"2\",\"3\",\"4\"]}", toStringBuilder.append("charArray", array).toString());
262         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
263         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
264         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
265     }
266 
267     @Test
268     void testCombineListAndEnum() {
269         final Teacher teacher = new Teacher();
270 
271         final Hobby[] teacherHobbies = new Hobby[3];
272         teacherHobbies[0] = Hobby.BOOK;
273         teacherHobbies[1] = Hobby.SPORT;
274         teacherHobbies[2] = Hobby.MUSIC;
275 
276         teacher.setHobbies(teacherHobbies);
277 
278         final Student john = new Student();
279         john.setHobbies(Arrays.asList(Hobby.BOOK, Hobby.MUSIC));
280 
281         final Student alice = new Student();
282         alice.setHobbies(new ArrayList<>());
283 
284         final Student bob = new Student();
285         bob.setHobbies(Collections.singletonList(Hobby.BOOK));
286 
287         final ArrayList<Student> students = new ArrayList<>();
288         students.add(john);
289         students.add(alice);
290         students.add(bob);
291 
292         final AcademyClass academyClass = new AcademyClass();
293         academyClass.setStudents(students);
294         academyClass.setTeacher(teacher);
295 
296         assertEquals(academyClass.toString(), "{\"students\":[{\"hobbies\":[\"BOOK\",\"MUSIC\"]},{\"hobbies\":[]},{\"hobbies\":[\"BOOK\"]}],\"teacher\":{\"hobbies\":[\"BOOK\",\"SPORT\",\"MUSIC\"]}}");
297     }
298 
299     @Test
300     void testDate() {
301         final Date now = new Date();
302         final Date afterNow = new Date(System.currentTimeMillis() + 1);
303 
304         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append(now).toString());
305 
306         assertEquals("{\"now\":\"" + now + "\"}", new ToStringBuilder(base).append("now", now).toString());
307         assertEquals("{\"now\":\"" + now + "\",\"after\":\"" + afterNow + "\"}",
308                 new ToStringBuilder(base).append("now", now).append("after", afterNow).toString());
309     }
310 
311     @Test
312     void testDoubleArray() {
313         final double[] array = { 1, 2, -3, 4 };
314         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
315         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
316         assertEquals("{\"doubleArray\":[1.0,2.0,-3.0,4.0]}", toStringBuilder.append("doubleArray", array).toString());
317         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
318         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
319         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
320     }
321 
322     @Test
323     void testFloatArray() {
324         final float[] array = { 1, 2, -3, 4 };
325         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
326         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
327         assertEquals("{\"floatArray\":[1.0,2.0,-3.0,4.0]}", toStringBuilder.append("floatArray", array).toString());
328         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
329         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
330         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
331     }
332 
333     @Test
334     void testIntArray() {
335         final int[] array = { 1, 2, -3, 4 };
336         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
337         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
338         assertEquals("{\"intArray\":[1,2,-3,4]}", toStringBuilder.append("intArray", array).toString());
339         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
340         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
341         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
342     }
343 
344     @Test
345     void testLANG1395() {
346         assertEquals("{\"name\":\"value\"}", new ToStringBuilder(base).append("name", "value").toString());
347         assertEquals("{\"name\":\"\"}", new ToStringBuilder(base).append("name", "").toString());
348         assertEquals("{\"name\":\"\\\"\"}", new ToStringBuilder(base).append("name", '"').toString());
349         assertEquals("{\"name\":\"\\\\\"}", new ToStringBuilder(base).append("name", '\\').toString());
350         assertEquals("{\"name\":\"Let's \\\"quote\\\" this\"}", new ToStringBuilder(base).append("name", "Let's \"quote\" this").toString());
351     }
352 
353     @Test
354     void testLANG1396() {
355         assertEquals("{\"Let's \\\"quote\\\" this\":\"value\"}", new ToStringBuilder(base).append("Let's \"quote\" this", "value").toString());
356     }
357 
358     @Test
359     void testList() {
360         final Student student = new Student();
361         final ArrayList<Hobby> objects = new ArrayList<>();
362 
363         objects.add(Hobby.BOOK);
364         objects.add(Hobby.SPORT);
365         objects.add(Hobby.MUSIC);
366 
367         student.setHobbies(objects);
368 
369         assertEquals(student.toString(), "{\"hobbies\":[\"BOOK\",\"SPORT\",\"MUSIC\"]}");
370         student.setHobbies(new ArrayList<>());
371         assertEquals(student.toString(), "{\"hobbies\":[]}");
372         student.setHobbies(null);
373         assertEquals(student.toString(), "{\"hobbies\":null}");
374     }
375 
376     @Test
377     void testLong() {
378         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append(3L).toString());
379 
380         assertEquals("{\"a\":3}", new ToStringBuilder(base).append("a", 3L)
381                 .toString());
382         assertEquals("{\"a\":3,\"b\":4}",
383                 new ToStringBuilder(base).append("a", 3L).append("b", 4L)
384                         .toString());
385     }
386 
387     @Test
388     void testLongArray() {
389         final long[] array = { 1, 2, -3, 4 };
390         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
391         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
392         assertEquals("{\"longArray\":[1,2,-3,4]}", toStringBuilder.append("longArray", array).toString());
393         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
394         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
395         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
396     }
397 
398     @Test
399     void testLongArrayArray() {
400         final long[][] array = { { 1, 2 }, null, { 5 } };
401         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
402         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
403         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
404         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[][]) null).toString());
405         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
406     }
407 
408     @Test
409     void testMapSkipNullKey() {
410         final Map<String, Object> map = new LinkedHashMap<>();
411         map.put("k1", "v1");
412         map.put(null, "v2");
413 
414         assertEquals("{\"map\":{\"k1\":\"v1\"}}",
415                 new ToStringBuilder(base).append("map", map).toString());
416     }
417 
418     @Test
419     void testNestedMaps() {
420         final Map<String, Object> innerMap = new LinkedHashMap<>();
421         innerMap.put("k2.1", "v2.1");
422         innerMap.put("k2.2", "v2.2");
423         final Map<String, Object> baseMap = new LinkedHashMap<>();
424         baseMap.put("k1", "v1");
425         baseMap.put("k2", innerMap);
426 
427         final InnerMapObject object = new InnerMapObject() {
428             @Override
429             public String toString() {
430                 return new ToStringBuilder(this).append("pid", this.pid).append("map", this.map).toString();
431             }
432         };
433         object.pid = "dummy-text";
434         object.map = baseMap;
435 
436         assertEquals("{\"object\":{\"pid\":\"dummy-text\",\"map\":{\"k1\":\"v1\",\"k2\":{\"k2.1\":\"v2.1\",\"k2.2\":\"v2.2\"}}}}",
437                 new ToStringBuilder(base).append("object", object).toString());
438     }
439 
440     @Test
441     void testNestingPerson() {
442         final Person p = new Person() {
443             @Override
444             public String toString() {
445                 return new ToStringBuilder(this).append("name", this.name).append("age", this.age).append("smoker", this.smoker).toString();
446             }
447         };
448         p.name = "Jane Doe";
449         p.age = 25;
450         p.smoker = true;
451 
452         final NestingPerson nestP = new NestingPerson();
453         nestP.pid = "#1@Jane";
454         nestP.person = p;
455 
456         assertEquals("{\"pid\":\"#1@Jane\",\"person\":{\"name\":\"Jane Doe\",\"age\":25,\"smoker\":true}}",
457                 new ToStringBuilder(nestP).append("pid", nestP.pid).append("person", nestP.person).toString());
458     }
459 
460     @Test
461     void testNull() {
462         assertEquals("null", new ToStringBuilder(null).toString());
463     }
464 
465     @Test
466     void testObject() {
467         final Integer i3 = Integer.valueOf(3);
468         final Integer i4 = Integer.valueOf(4);
469         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append((Object) null).toString());
470         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append(i3).toString());
471         assertEquals("{\"a\":null}", new ToStringBuilder(base).append("a", (Object) null).toString());
472         assertEquals("{\"a\":3}", new ToStringBuilder(base).append("a", i3).toString());
473         assertEquals("{\"a\":3,\"b\":4}", new ToStringBuilder(base).append("a", i3).append("b", i4).toString());
474         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append("a", i3, false).toString());
475         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append("a", new ArrayList<>(), false).toString());
476         assertEquals("{\"a\":[]}", new ToStringBuilder(base).append("a", new ArrayList<>(), true).toString());
477         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append("a", new HashMap<>(), false).toString());
478         assertEquals("{\"a\":{}}", new ToStringBuilder(base).append("a", new HashMap<>(), true).toString());
479         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append("a", (Object) new String[0], false).toString());
480         assertEquals("{\"a\":[]}", new ToStringBuilder(base).append("a", (Object) new String[0], true).toString());
481         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append("a", (Object) new int[] { 1, 2, 3 }, false).toString());
482         assertEquals("{\"a\":[1,2,3]}", new ToStringBuilder(base).append("a", (Object) new int[] { 1, 2, 3 }, true).toString());
483         assertThrows(UnsupportedOperationException.class,
484                 () -> new ToStringBuilder(base).append("a", (Object) new String[] { "v", "x", "y", "z" }, false).toString());
485         assertEquals("{\"a\":[\"v\",\"x\",\"y\",\"z\"]}", new ToStringBuilder(base).append("a", (Object) new String[] { "v", "x", "y", "z" }, true).toString());
486     }
487 
488     @Test
489     void testObjectArray() {
490         final Object[] array = { null, base, new int[] { 3, 6 } };
491         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
492         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
493         assertEquals("{\"objectArray\":[null,5,[3,6]]}", toStringBuilder.append("objectArray", array).toString());
494         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
495         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object[]) null).toString());
496         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
497     }
498 
499     @Test
500     void testObjectWithInnerMap() {
501         final Map<String, Object> map = new LinkedHashMap<>();
502         map.put("k1", "value1");
503         map.put("k2", 2);
504 
505         final InnerMapObject object = new InnerMapObject() {
506             @Override
507             public String toString() {
508                 return new ToStringBuilder(this).append("pid", this.pid).append("map", this.map).toString();
509             }
510         };
511         object.pid = "dummy-text";
512         object.map = map;
513 
514         assertEquals("{\"object\":{\"pid\":\"dummy-text\",\"map\":{\"k1\":\"value1\",\"k2\":2}}}",
515                 new ToStringBuilder(base).append("object", object).toString());
516     }
517 
518     @Test
519     void testPerson() {
520         final Person p = new Person();
521         p.name = "Jane Doe";
522         p.age = 25;
523         p.smoker = true;
524 
525         assertEquals(
526                 "{\"name\":\"Jane Doe\",\"age\":25,\"smoker\":true}",
527                 new ToStringBuilder(p).append("name", p.name)
528                         .append("age", p.age).append("smoker", p.smoker)
529                         .toString());
530     }
531 
532     @Test
533     void testRootMap() {
534         final Map<String, Object> map = new LinkedHashMap<>();
535         map.put("k1", "v1");
536         map.put("k2", 2);
537 
538         assertEquals("{\"map\":{\"k1\":\"v1\",\"k2\":2}}",
539                 new ToStringBuilder(base).append("map", map).toString());
540     }
541 
542     @Test
543     void testShortArray() {
544         final short[] array = { 1, 2, -3, 4 };
545         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
546         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
547         assertEquals("{\"shortArray\":[1,2,-3,4]}", toStringBuilder.append("shortArray", array).toString());
548         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
549         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
550         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
551     }
552 }