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    *      http://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.juneau.json;
18  
19  import static org.apache.juneau.TestUtils.*;
20  import static org.apache.juneau.commons.utils.CollectionUtils.*;
21  import static org.junit.jupiter.api.Assertions.*;
22  
23  import java.net.*;
24  import java.util.*;
25  
26  import org.apache.juneau.*;
27  import org.apache.juneau.annotation.*;
28  import org.apache.juneau.collections.*;
29  import org.junit.jupiter.api.*;
30  
31  class Common_Test extends TestBase {
32  
33  	//====================================================================================================
34  	// Trim nulls from beans
35  	//====================================================================================================
36  	@Test void a01_trimNullsFromBeans() throws Exception {
37  		var s = JsonSerializer.create().json5();
38  		var p = JsonParser.DEFAULT;
39  		var t1 = A.create();
40  
41  		var r = s.build().serialize(t1);
42  		assertEquals("{s2:'s2'}", r);
43  		var t2 = p.parse(r, A.class);
44  		assertEquals(json(t2), json(t1));
45  
46  		s.keepNullProperties();
47  		r = s.build().serialize(t1);
48  		assertEquals("{s1:null,s2:'s2'}", r);
49  		t2 = p.parse(r, A.class);
50  		assertEquals(json(t2), json(t1));
51  	}
52  
53  	public static class A {
54  		public String s1, s2;
55  
56  		public static A create() {
57  			var t = new A();
58  			t.s2 = "s2";
59  			return t;
60  		}
61  	}
62  
63  	//====================================================================================================
64  	// Trim empty maps
65  	//====================================================================================================
66  	@Test void a02_trimEmptyMaps() throws Exception {
67  		var s = JsonSerializer.create().json5();
68  		var p = JsonParser.DEFAULT;
69  		var t1 = B.create();
70  		var r = s.build().serialize(t1);
71  
72  		assertEquals("{f1:{},f2:{f2a:null,f2b:{s2:'s2'}}}", r);
73  		var t2 = p.parse(r, B.class);
74  		assertEquals(json(t2), json(t1));
75  
76  		s.trimEmptyMaps();
77  		r = s.build().serialize(t1);
78  		assertEquals("{f2:{f2a:null,f2b:{s2:'s2'}}}", r);
79  		t2 = p.parse(r, B.class);
80  		assertNull(t2.f1);
81  	}
82  
83  	public static class B {
84  		public TreeMap<String,A> f1, f2;
85  
86  		public static B create() {
87  			var t = new B();
88  			t.f1 = new TreeMap<>();
89  			t.f2 = new TreeMap<>(){{put("f2a",null);put("f2b",A.create());}};
90  			return t;
91  		}
92  	}
93  
94  	//====================================================================================================
95  	// Trim empty lists
96  	//====================================================================================================
97  	@Test void a03_trimEmptyLists() throws Exception {
98  		var s = JsonSerializer.create().json5();
99  		var p = JsonParser.DEFAULT;
100 		var t1 = C.create();
101 		var r = s.build().serialize(t1);
102 
103 		assertEquals("{f1:[],f2:[null,{s2:'s2'}]}", r);
104 		var t2 = p.parse(r, C.class);
105 		assertEquals(json(t2), json(t1));
106 
107 		s.trimEmptyCollections();
108 		r = s.build().serialize(t1);
109 		assertEquals("{f2:[null,{s2:'s2'}]}", r);
110 		t2 = p.parse(r, C.class);
111 		assertNull(t2.f1);
112 	}
113 
114 	public static class C {
115 		public List<A> f1, f2;
116 
117 		public static C create() {
118 			var t = new C();
119 			t.f1 = l();
120 			t.f2 = l(null,A.create());
121 			return t;
122 		}
123 	}
124 
125 	//====================================================================================================
126 	// Trim empty arrays
127 	//====================================================================================================
128 	@Test void a04_trimEmptyArrays() throws Exception {
129 		var s = JsonSerializer.create().json5();
130 		var p = JsonParser.DEFAULT;
131 		var t1 = D.create();
132 		var r = s.build().serialize(t1);
133 
134 		assertEquals("{f1:[],f2:[null,{s2:'s2'}]}", r);
135 		var t2 = p.parse(r, D.class);
136 		assertEquals(json(t2), json(t1));
137 
138 		s.trimEmptyCollections();
139 		r = s.build().serialize(t1);
140 		assertEquals("{f2:[null,{s2:'s2'}]}", r);
141 		t2 = p.parse(r, D.class);
142 		assertNull(t2.f1);
143 	}
144 
145 	public static class D {
146 		public A[] f1, f2;
147 
148 		public static D create() {
149 			var t = new D();
150 			t.f1 = a();
151 			t.f2 = a(null, A.create());
152 			return t;
153 		}
154 	}
155 
156 	//====================================================================================================
157 	// @Beanp.bpi annotation.
158 	//====================================================================================================
159 	@Test void a05_beanPropertyProperies() throws Exception {
160 		var s = Json5Serializer.DEFAULT;
161 		var t = new E1();
162 		var r = s.serialize(t);
163 
164 		assertEquals("{x1:{f1:1},x2:{f1:1},x3:[{f1:1}],x4:[{f1:1}],x5:[{f1:1}],x6:[{f1:1}]}", r);
165 		r = s.getSchemaSerializer().serialize(t);
166 		assertEquals(r.indexOf("f2"), -1);
167 	}
168 
169 	public static class E1 {
170 		@Beanp(properties="f1") public E2 x1 = new E2();
171 		@Beanp(properties="f1") public Map<String,Integer> x2 = m("f1",1,"f2",2);
172 		@Beanp(properties="f1") public E2[] x3 = {new E2()};
173 		@Beanp(properties="f1") public List<E2> x4 = l(new E2());
174 		@Beanp(properties="f1") public JsonMap[] x5 = {JsonMap.of("f1",1,"f2",2)};
175 		@Beanp(properties="f1") public List<JsonMap> x6 = l(JsonMap.of("f1",1,"f2",2));
176 	}
177 
178 	public static class E2 {
179 		public int f1 = 1;
180 		public int f2 = 2;
181 	}
182 
183 	//====================================================================================================
184 	// @Beanp.bpi annotation on list of beans.
185 	//====================================================================================================
186 	@Test void a06_beanPropertyProperiesOnListOfBeans() throws Exception {
187 		var s = Json5Serializer.DEFAULT;
188 		var l = new LinkedList<>();
189 		var t = new F();
190 		t.x1.add(new F());
191 		l.add(t);
192 		var json = s.serialize(l);
193 		assertEquals("[{x1:[{x2:2}],x2:2}]", json);
194 	}
195 
196 	public static class F {
197 		@Beanp(properties="x2") public List<F> x1 = new LinkedList<>();
198 		public int x2 = 2;
199 	}
200 
201 	//====================================================================================================
202 	// Test that URLs and URIs are serialized and parsed correctly.
203 	//====================================================================================================
204 	@Test void a07_uRIAttr() throws Exception {
205 		var s = Json5Serializer.DEFAULT;
206 		var p = JsonParser.DEFAULT;
207 
208 		var t = new G();
209 		t.uri = new URI("http://uri");
210 		t.f1 = new URI("http://f1");
211 		t.f2 = url("http://f2");
212 
213 		var json = s.serialize(t);
214 		t = p.parse(json, G.class);
215 		assertEquals("http://uri", t.uri.toString());
216 		assertEquals("http://f1", t.f1.toString());
217 		assertEquals("http://f2", t.f2.toString());
218 	}
219 
220 	public static class G {
221 		public URI uri;
222 		public URI f1;
223 		public URL f2;
224 	}
225 
226 	//====================================================================================================
227 	// Recursion
228 	//====================================================================================================
229 	@Test void a08_recursion() throws Exception {
230 		var s = JsonSerializer.create().json5().maxDepth(Integer.MAX_VALUE);
231 
232 		var r1 = new R1();
233 		var r2 = new R2();
234 		var r3 = new R3();
235 		r1.r2 = r2;
236 		r2.r3 = r3;
237 		r3.r1 = r1;
238 
239 		// No recursion detection
240 		assertThrowsWithMessage(Exception.class, "It's recommended you use the BeanTraverseContext.BEANTRAVERSE_detectRecursions setting to help locate the loop.", ()->s.build().serialize(r1));
241 
242 		// Recursion detection, no ignore
243 		s.detectRecursions();
244 		assertThrowsWithMessage(Exception.class, "$R1", ()->s.build().serialize(r1));
245 
246 		s.ignoreRecursions();
247 		assertEquals("{name:'foo',r2:{name:'bar',r3:{name:'baz'}}}", s.build().serialize(r1));
248 
249 		// Make sure this doesn't blow up.
250 		s.build().getSchemaSerializer().serialize(r1);
251 	}
252 
253 	public static class R1 {
254 		public String name = "foo";
255 		public R2 r2;
256 	}
257 	public static class R2 {
258 		public String name = "bar";
259 		public R3 r3;
260 	}
261 	public static class R3 {
262 		public String name = "baz";
263 		public R1 r1;
264 	}
265 
266 	//====================================================================================================
267 	// Basic bean
268 	//====================================================================================================
269 	@Test void a09_basicBean() throws Exception {
270 		var s = JsonSerializer.create().json5().keepNullProperties().sortProperties().build();
271 
272 		var a = new J();
273 		a.setF1("J");
274 		a.setF2(100);
275 		a.setF3(true);
276 		assertEquals("{f1:'J',f2:100,f3:true}", s.serialize(a));
277 	}
278 
279 	public static class J {
280 		private String f1;
281 		public String getF1() { return f1; }
282 		public void setF1(String v) { f1 = v; }
283 
284 		private int f2 = -1;
285 		public int getF2() { return f2; }
286 		public void setF2(int v) { f2 = v; }
287 
288 		private boolean f3;
289 		public boolean isF3() { return f3; }
290 		public void setF3(boolean v) { f3 = v; }
291 
292 		@Override /* Overridden from Object */
293 		public String toString() {
294 			return ("J(f1: " + this.getF1() + ", f2: " + this.getF2() + ")");
295 		}
296 	}
297 
298 	//====================================================================================================
299 	// @Beanp(format) tests
300 	//====================================================================================================
301 
302 	@Test
303 	void a10_beanpFormat() throws Exception {
304 		var s = JsonSerializer.create().json5().sortProperties().build();
305 
306 		var bean = new K();
307 		var json = s.serialize(bean);
308 
309 		// Verify all formatted fields are serialized correctly
310 		assertTrue(json.contains("floatField:'3.14'"));
311 		assertTrue(json.contains("floatWithCurrency:'$19.99'"));
312 		assertTrue(json.contains("doubleField:'2.718'"));
313 		assertTrue(json.contains("privateField:'9.88'"));
314 		assertTrue(json.contains("intField:'00042'"));
315 		assertTrue(json.contains("longField:'0000001234567890'"));
316 		assertTrue(json.contains("hexField:'0xFF00FF'"));
317 		assertTrue(json.contains("scientificField:'6.022e+23'") || json.contains("scientificField:'6.022E+23'"));
318 		assertTrue(json.contains("name:'Test'"));
319 	}
320 
321 	public static class K {
322 		@Beanp(format="%.2f")
323 		public float floatField = 3.14159f;
324 
325 		@Beanp(format="$%.2f")
326 		public float floatWithCurrency = 19.987f;
327 
328 		@Beanp(format="%.3f")
329 		public double doubleField = 2.71828;
330 
331 		@Beanp(format="%05d")
332 		public int intField = 42;
333 
334 		@Beanp(format="%016d")
335 		public long longField = 1234567890L;
336 
337 		@Beanp(format="0x%06X")
338 		public int hexField = 0xFF00FF;
339 
340 		@Beanp(format="%.3e")
341 		public double scientificField = 6.02214076e23;
342 
343 		public String name = "Test";
344 
345 		@Beanp(format="%.2f")
346 		private float privateField = 9.876f;
347 
348 		public float getPrivateField() { return privateField; }
349 		public void setPrivateField(float f) { privateField = f; }
350 	}
351 }