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          /**
83           * Test String field.
84           */
85          String pid;
86  
87          /**
88           * Test inner map field.
89           */
90          Map<String, Object> map;
91      }
92  
93      /**
94       * An object with nested object structures used to test {@code ToStringStyle.JsonToStringStyle}.
95       */
96      static class NestingPerson {
97  
98          /**
99           * Test String field.
100          */
101         String pid;
102 
103         /**
104          * Test nested object field.
105          */
106         Person person;
107     }
108 
109     static class Student {
110         List<Hobby> hobbies;
111 
112         public List<Hobby> getHobbies() {
113             return hobbies;
114         }
115 
116         public void setHobbies(final List<Hobby> hobbies) {
117             this.hobbies = hobbies;
118         }
119 
120         @Override
121         public String toString() {
122             return ToStringBuilder.reflectionToString(this);
123         }
124     }
125 
126     static class Teacher {
127         Hobby[] hobbies;
128 
129         public Hobby[] getHobbies() {
130             return hobbies;
131         }
132 
133         public void setHobbies(final Hobby[] hobbies) {
134             this.hobbies = hobbies;
135         }
136 
137         @Override
138         public String toString() {
139             return ToStringBuilder.reflectionToString(this);
140         }
141     }
142 
143     private final Integer base = Integer.valueOf(5);
144 
145     @BeforeEach
146     public void setUp() {
147         ToStringBuilder.setDefaultStyle(ToStringStyle.JSON_STYLE);
148     }
149 
150     @AfterEach
151     public void tearDown() {
152         ToStringBuilder.setDefaultStyle(ToStringStyle.DEFAULT_STYLE);
153     }
154 
155     @Test
156     void testAppendSuper() {
157         assertEquals(
158                 "{}",
159                 new ToStringBuilder(base).appendSuper(
160                         "Integer@8888[" + System.lineSeparator() + "]")
161                         .toString());
162         assertEquals(
163                 "{}",
164                 new ToStringBuilder(base).appendSuper(
165                         "Integer@8888[" + System.lineSeparator() + "  null"
166                                 + System.lineSeparator() + "]").toString());
167         assertEquals(
168                 "{\"a\":\"hello\"}",
169                 new ToStringBuilder(base)
170                         .appendSuper(
171                                 "Integer@8888[" + System.lineSeparator()
172                                         + "]").append("a", "hello").toString());
173         assertEquals(
174                 "{\"a\":\"hello\"}",
175                 new ToStringBuilder(base)
176                         .appendSuper(
177                                 "Integer@8888[" + System.lineSeparator()
178                                         + "  null" + System.lineSeparator()
179                                         + "]").append("a", "hello").toString());
180         assertEquals("{\"a\":\"hello\"}", new ToStringBuilder(base)
181                 .appendSuper(null).append("a", "hello").toString());
182 
183         assertEquals("{\"a\":\"hello\",\"b\":\"world\"}", new ToStringBuilder(base)
184                 .appendSuper("{\"a\":\"hello\"}").append("b", "world").toString());
185     }
186 
187     @Test
188     void testArray() {
189         final Person p = new Person();
190         p.name = "Jane Doe";
191         p.age = 25;
192         p.smoker = true;
193 
194         assertEquals(
195                 "{\"name\":\"Jane Doe\",\"age\":25,\"smoker\":true,\"groups\":['admin', 'manager', 'user']}",
196                 new ToStringBuilder(p).append("name", p.name)
197                         .append("age", p.age).append("smoker", p.smoker)
198                         .append("groups", new Object() {
199                             @Override
200                             public String toString() {
201                                 return "['admin', 'manager', 'user']";
202                             }
203                         })
204                         .toString());
205     }
206 
207     @Test
208     void testArrayEnum() {
209         final Teacher teacher = new Teacher();
210         final Hobby[] hobbies = new Hobby[3];
211         hobbies[0] = Hobby.BOOK;
212         hobbies[1] = Hobby.SPORT;
213         hobbies[2] = Hobby.MUSIC;
214 
215         teacher.setHobbies(hobbies);
216 
217         assertEquals(teacher.toString(), "{\"hobbies\":[\"BOOK\",\"SPORT\",\"MUSIC\"]}");
218         teacher.setHobbies(new Hobby[0]);
219         assertEquals(teacher.toString(), "{\"hobbies\":[]}");
220         teacher.setHobbies(null);
221         assertEquals(teacher.toString(), "{\"hobbies\":null}");
222     }
223 
224     @Test
225     void testBlank() {
226         assertEquals("{}", new ToStringBuilder(base).toString());
227     }
228 
229     @Test
230     void testBooleanArray() {
231         final boolean[] array = { true, false };
232         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
233         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
234         assertEquals("{\"booleanArray\":[true,false]}", toStringBuilder.append("booleanArray", array).toString());
235         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
236         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
237         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
238     }
239 
240     @Test
241     void testByteArray() {
242         final byte[] array = { 1, 2, -3, 4 };
243         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
244         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
245         assertEquals("{\"byteArray\":[1,2,-3,4]}", toStringBuilder.append("byteArray", array).toString());
246         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
247         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
248         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
249     }
250 
251     @Test
252     void testChar() {
253         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append('A').toString());
254         assertEquals("{\"a\":\"A\"}", new ToStringBuilder(base).append("a", 'A').toString());
255         assertEquals("{\"a\":\"A\",\"b\":\"B\"}", new ToStringBuilder(base).append("a", 'A').append("b", 'B').toString());
256     }
257 
258     @Test
259     void testCharArray() {
260         final char[] array = { '1', '2', '3', '4' };
261         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
262         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
263         assertEquals("{\"charArray\":[\"1\",\"2\",\"3\",\"4\"]}", toStringBuilder.append("charArray", array).toString());
264         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
265         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
266         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
267     }
268 
269     @Test
270     void testCombineListAndEnum() {
271         final Teacher teacher = new Teacher();
272 
273         final Hobby[] teacherHobbies = new Hobby[3];
274         teacherHobbies[0] = Hobby.BOOK;
275         teacherHobbies[1] = Hobby.SPORT;
276         teacherHobbies[2] = Hobby.MUSIC;
277 
278         teacher.setHobbies(teacherHobbies);
279 
280         final Student john = new Student();
281         john.setHobbies(Arrays.asList(Hobby.BOOK, Hobby.MUSIC));
282 
283         final Student alice = new Student();
284         alice.setHobbies(new ArrayList<>());
285 
286         final Student bob = new Student();
287         bob.setHobbies(Collections.singletonList(Hobby.BOOK));
288 
289         final ArrayList<Student> students = new ArrayList<>();
290         students.add(john);
291         students.add(alice);
292         students.add(bob);
293 
294         final AcademyClass academyClass = new AcademyClass();
295         academyClass.setStudents(students);
296         academyClass.setTeacher(teacher);
297 
298         assertEquals(academyClass.toString(), "{\"students\":[{\"hobbies\":[\"BOOK\",\"MUSIC\"]},{\"hobbies\":[]},{\"hobbies\":[\"BOOK\"]}],\"teacher\":{\"hobbies\":[\"BOOK\",\"SPORT\",\"MUSIC\"]}}");
299     }
300 
301     @Test
302     void testDate() {
303         final Date now = new Date();
304         final Date afterNow = new Date(System.currentTimeMillis() + 1);
305 
306         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append(now).toString());
307 
308         assertEquals("{\"now\":\"" + now + "\"}", new ToStringBuilder(base).append("now", now).toString());
309         assertEquals("{\"now\":\"" + now + "\",\"after\":\"" + afterNow + "\"}",
310                 new ToStringBuilder(base).append("now", now).append("after", afterNow).toString());
311     }
312 
313     @Test
314     void testDoubleArray() {
315         final double[] array = { 1, 2, -3, 4 };
316         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
317         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
318         assertEquals("{\"doubleArray\":[1.0,2.0,-3.0,4.0]}", toStringBuilder.append("doubleArray", array).toString());
319         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
320         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
321         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
322     }
323 
324     @Test
325     void testFloatArray() {
326         final float[] array = { 1, 2, -3, 4 };
327         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
328         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
329         assertEquals("{\"floatArray\":[1.0,2.0,-3.0,4.0]}", toStringBuilder.append("floatArray", array).toString());
330         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
331         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
332         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
333     }
334 
335     @Test
336     void testIntArray() {
337         final int[] array = { 1, 2, -3, 4 };
338         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
339         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
340         assertEquals("{\"intArray\":[1,2,-3,4]}", toStringBuilder.append("intArray", array).toString());
341         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
342         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
343         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
344     }
345 
346     @Test
347     void testLANG1395() {
348         assertEquals("{\"name\":\"value\"}", new ToStringBuilder(base).append("name", "value").toString());
349         assertEquals("{\"name\":\"\"}", new ToStringBuilder(base).append("name", "").toString());
350         assertEquals("{\"name\":\"\\\"\"}", new ToStringBuilder(base).append("name", '"').toString());
351         assertEquals("{\"name\":\"\\\\\"}", new ToStringBuilder(base).append("name", '\\').toString());
352         assertEquals("{\"name\":\"Let's \\\"quote\\\" this\"}", new ToStringBuilder(base).append("name", "Let's \"quote\" this").toString());
353     }
354 
355     @Test
356     void testLANG1396() {
357         assertEquals("{\"Let's \\\"quote\\\" this\":\"value\"}", new ToStringBuilder(base).append("Let's \"quote\" this", "value").toString());
358     }
359 
360     @Test
361     void testList() {
362         final Student student = new Student();
363         final ArrayList<Hobby> objects = new ArrayList<>();
364 
365         objects.add(Hobby.BOOK);
366         objects.add(Hobby.SPORT);
367         objects.add(Hobby.MUSIC);
368 
369         student.setHobbies(objects);
370 
371         assertEquals(student.toString(), "{\"hobbies\":[\"BOOK\",\"SPORT\",\"MUSIC\"]}");
372         student.setHobbies(new ArrayList<>());
373         assertEquals(student.toString(), "{\"hobbies\":[]}");
374         student.setHobbies(null);
375         assertEquals(student.toString(), "{\"hobbies\":null}");
376     }
377 
378     @Test
379     void testLong() {
380         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append(3L).toString());
381 
382         assertEquals("{\"a\":3}", new ToStringBuilder(base).append("a", 3L)
383                 .toString());
384         assertEquals("{\"a\":3,\"b\":4}",
385                 new ToStringBuilder(base).append("a", 3L).append("b", 4L)
386                         .toString());
387     }
388 
389     @Test
390     void testLongArray() {
391         final long[] array = { 1, 2, -3, 4 };
392         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
393         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
394         assertEquals("{\"longArray\":[1,2,-3,4]}", toStringBuilder.append("longArray", array).toString());
395         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
396         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
397         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
398     }
399 
400     @Test
401     void testLongArrayArray() {
402         final long[][] array = { { 1, 2 }, null, { 5 } };
403         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
404         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
405         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
406         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[][]) null).toString());
407         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
408     }
409 
410     @Test
411     void testMapSkipNullKey() {
412         final Map<String, Object> map = new LinkedHashMap<>();
413         map.put("k1", "v1");
414         map.put(null, "v2");
415 
416         assertEquals("{\"map\":{\"k1\":\"v1\"}}",
417                 new ToStringBuilder(base).append("map", map).toString());
418     }
419 
420     @Test
421     void testNestedMaps() {
422         final Map<String, Object> innerMap = new LinkedHashMap<>();
423         innerMap.put("k2.1", "v2.1");
424         innerMap.put("k2.2", "v2.2");
425         final Map<String, Object> baseMap = new LinkedHashMap<>();
426         baseMap.put("k1", "v1");
427         baseMap.put("k2", innerMap);
428 
429         final InnerMapObject object = new InnerMapObject() {
430             @Override
431             public String toString() {
432                 return new ToStringBuilder(this).append("pid", this.pid).append("map", this.map).toString();
433             }
434         };
435         object.pid = "dummy-text";
436         object.map = baseMap;
437 
438         assertEquals("{\"object\":{\"pid\":\"dummy-text\",\"map\":{\"k1\":\"v1\",\"k2\":{\"k2.1\":\"v2.1\",\"k2.2\":\"v2.2\"}}}}",
439                 new ToStringBuilder(base).append("object", object).toString());
440     }
441 
442     @Test
443     void testNestingPerson() {
444         final Person p = new Person() {
445             @Override
446             public String toString() {
447                 return new ToStringBuilder(this).append("name", this.name).append("age", this.age).append("smoker", this.smoker).toString();
448             }
449         };
450         p.name = "Jane Doe";
451         p.age = 25;
452         p.smoker = true;
453 
454         final NestingPerson nestP = new NestingPerson();
455         nestP.pid = "#1@Jane";
456         nestP.person = p;
457 
458         assertEquals("{\"pid\":\"#1@Jane\",\"person\":{\"name\":\"Jane Doe\",\"age\":25,\"smoker\":true}}",
459                 new ToStringBuilder(nestP).append("pid", nestP.pid).append("person", nestP.person).toString());
460     }
461 
462     @Test
463     void testNull() {
464         assertEquals("null", new ToStringBuilder(null).toString());
465     }
466 
467     @Test
468     void testObject() {
469         final Integer i3 = Integer.valueOf(3);
470         final Integer i4 = Integer.valueOf(4);
471         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append((Object) null).toString());
472         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append(i3).toString());
473         assertEquals("{\"a\":null}", new ToStringBuilder(base).append("a", (Object) null).toString());
474         assertEquals("{\"a\":3}", new ToStringBuilder(base).append("a", i3).toString());
475         assertEquals("{\"a\":3,\"b\":4}", new ToStringBuilder(base).append("a", i3).append("b", i4).toString());
476         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append("a", i3, false).toString());
477         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append("a", new ArrayList<>(), false).toString());
478         assertEquals("{\"a\":[]}", new ToStringBuilder(base).append("a", new ArrayList<>(), true).toString());
479         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append("a", new HashMap<>(), false).toString());
480         assertEquals("{\"a\":{}}", new ToStringBuilder(base).append("a", new HashMap<>(), true).toString());
481         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append("a", (Object) new String[0], false).toString());
482         assertEquals("{\"a\":[]}", new ToStringBuilder(base).append("a", (Object) new String[0], true).toString());
483         assertThrows(UnsupportedOperationException.class, () -> new ToStringBuilder(base).append("a", (Object) new int[] { 1, 2, 3 }, false).toString());
484         assertEquals("{\"a\":[1,2,3]}", new ToStringBuilder(base).append("a", (Object) new int[] { 1, 2, 3 }, true).toString());
485         assertThrows(UnsupportedOperationException.class,
486                 () -> new ToStringBuilder(base).append("a", (Object) new String[] { "v", "x", "y", "z" }, false).toString());
487         assertEquals("{\"a\":[\"v\",\"x\",\"y\",\"z\"]}", new ToStringBuilder(base).append("a", (Object) new String[] { "v", "x", "y", "z" }, true).toString());
488     }
489 
490     @Test
491     void testObjectArray() {
492         final Object[] array = { null, base, new int[] { 3, 6 } };
493         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
494         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
495         assertEquals("{\"objectArray\":[null,5,[3,6]]}", toStringBuilder.append("objectArray", array).toString());
496         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
497         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object[]) null).toString());
498         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
499     }
500 
501     @Test
502     void testObjectWithInnerMap() {
503         final Map<String, Object> map = new LinkedHashMap<>();
504         map.put("k1", "value1");
505         map.put("k2", 2);
506 
507         final InnerMapObject object = new InnerMapObject() {
508             @Override
509             public String toString() {
510                 return new ToStringBuilder(this).append("pid", this.pid).append("map", this.map).toString();
511             }
512         };
513         object.pid = "dummy-text";
514         object.map = map;
515 
516         assertEquals("{\"object\":{\"pid\":\"dummy-text\",\"map\":{\"k1\":\"value1\",\"k2\":2}}}",
517                 new ToStringBuilder(base).append("object", object).toString());
518     }
519 
520     @Test
521     void testPerson() {
522         final Person p = new Person();
523         p.name = "Jane Doe";
524         p.age = 25;
525         p.smoker = true;
526 
527         assertEquals(
528                 "{\"name\":\"Jane Doe\",\"age\":25,\"smoker\":true}",
529                 new ToStringBuilder(p).append("name", p.name)
530                         .append("age", p.age).append("smoker", p.smoker)
531                         .toString());
532     }
533 
534     @Test
535     void testRootMap() {
536         final Map<String, Object> map = new LinkedHashMap<>();
537         map.put("k1", "v1");
538         map.put("k2", 2);
539 
540         assertEquals("{\"map\":{\"k1\":\"v1\",\"k2\":2}}",
541                 new ToStringBuilder(base).append("map", map).toString());
542     }
543 
544     @Test
545     void testShortArray() {
546         final short[] array = { 1, 2, -3, 4 };
547         final ToStringBuilder toStringBuilder = new ToStringBuilder(base);
548         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append(array).toString());
549         assertEquals("{\"shortArray\":[1,2,-3,4]}", toStringBuilder.append("shortArray", array).toString());
550         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
551         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((long[]) null).toString());
552         assertThrows(UnsupportedOperationException.class, () -> toStringBuilder.append((Object) array).toString());
553     }
554 }