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