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.collections;
18  
19  import static org.apache.juneau.commons.utils.PredicateUtils.*;
20  import static org.apache.juneau.commons.utils.StringUtils.*;
21  import static org.apache.juneau.commons.utils.ThrowableUtils.*;
22  import static org.apache.juneau.commons.utils.Utils.*;
23  
24  import java.io.*;
25  import java.lang.reflect.*;
26  import java.util.*;
27  import java.util.function.*;
28  
29  import org.apache.juneau.*;
30  import org.apache.juneau.commons.utils.*;
31  import org.apache.juneau.json.*;
32  import org.apache.juneau.marshaller.*;
33  import org.apache.juneau.objecttools.*;
34  import org.apache.juneau.parser.*;
35  import org.apache.juneau.serializer.*;
36  
37  /**
38   * Java implementation of a JSON array.
39   *
40   * <p>
41   * An extension of {@link LinkedList}, so all methods available to in that class are also available to this class.
42   *
43   * <p>
44   * Note that the use of this class is optional for generating JSON.  The serializers will accept any objects that implement the
45   * {@link Collection} interface.  But this class provides some useful additional functionality when working with JSON
46   * models constructed from Java Collections Framework objects.  For example, a constructor is provided for converting a
47   * JSON array string directly into a {@link List}.  It also contains accessor methods for to avoid common typecasting
48   * when accessing elements in a list.
49   *
50   * <h5 class='section'>Example:</h5>
51   * <p class='bjava'>
52   * 	<jc>// Construct an empty List</jc>
53   * 	JsonList <jv>list</jv> = JsonList.<jsm>of</jsm>();
54   *
55   * 	<jc>// Construct a list of objects using various methods</jc>
56   * 	<jv>list</jv> = JsonList.<jsm>of</jsm>().a(<js>"foo"</js>).a(123).a(<jk>true</jk>);
57   * 	<jv>list</jv> = JsonList.<jsm>of</jsm>().a(<js>"foo"</js>, 123, <jk>true</jk>);  <jc>// Equivalent</jc>
58   * 	<jv>list</jv> = JsonList.<jsm>of</jsm>(<js>"foo"</js>, 123, <jk>true</jk>);  <jc>// Equivalent</jc>
59   *
60   * 	<jc>// Construct a list of integers from JSON</jc>
61   * 	<jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[1,2,3]"</js>);
62   *
63   * 	<jc>// Construct a list of generic JsonMap objects from JSON</jc>
64   * 	<jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[{foo:'bar'},{baz:'bing'}]"</js>);
65   *
66   * 	<jc>// Construct a list of integers from XML</jc>
67   * 	String <jv>xml</jv> = <js>"&lt;array&gt;&lt;number&gt;1&lt;/number&gt;&lt;number&gt;2&lt;/number&gt;&lt;number&gt;3&lt;/number&gt;&lt;/array&gt;"</js>;
68   * 	<jv>list</jv> = JsonList.<jsm>of</jsm>(<jv>xml</jv>, XmlParser.<jsf>DEFAULT</jsf>);
69   * 	<jv>list</jv> = (List)XmlParser.<jsf>DEFAULT</jsf>.parse(<jv>xml</jv>);  <jc>// Equivalent</jc>
70   * 	<jv>list</jv> = (List)XmlParser.<jsf>DEFAULT</jsf>.parse(Object.<jk>class</jk>, <jv>xml</jv>);  <jc>// Equivalent</jc>
71   * 	<jv>list</jv> = XmlParser.<jsf>DEFAULT</jsf>.parse(List.<jk>class</jk>, <jv>xml</jv>);  <jc>// Equivalent</jc>
72   * 	<jv>list</jv> = XmlParser.<jsf>DEFAULT</jsf>.parse(JsonList.<jk>class</jk>, <jv>xml</jv>);  <jc>// Equivalent</jc>
73   *
74   * 	<jc>// Construct JSON from JsonList</jc>
75   * 	<jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[{foo:'bar'},{baz:'bing'}]"</js>);
76   * 	String <jv>json</jv> = <jv>list</jv>.toString();  <jc>// Produces "[{foo:'bar'},{baz:'bing'}]"</jc>
77   * 	<jv>json</jv> = <jv>list</jv>.toString(JsonSerializer.<jsf>DEFAULT</jsf>);  <jc>// Equivalent</jc>
78   * 	<jv>json</jv> = JsonSerializer.<jsf>DEFAULT</jsf>.serialize(<jv>list</jv>);  <jc>// Equivalent</jc>
79   *
80   * 	<jc>// Get one of the entries in the list as an Integer</jc>
81   * 	<jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[1,2,3]"</js>);
82   * 	Integer <jv>integer</jv> = <jv>list</jv>.getInt(1);
83   * 	<jv>list</jv> = <jv>list</jv>.get(Integer.<jk>class</jk>, 1);  <jc>// Equivalent</jc>
84   *
85   * 	<jc>// Get one of the entries in the list as an Float</jc>
86   * 	<jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[1,2,3]"</js>);
87   * 	Float <jv>_float</jv> = <jv>list</jv>.getFloat(1); <jc>// Returns 2f </jc>
88   * 	<jv>_float</jv> = <jv>list</jv>.get(Float.<jk>class</jk>, 1);  <jc>// Equivalent</jc>
89   *
90   * 	<jc>// Same as above, except converted to a String</jc>
91   * 	<jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[1,2,3]"</js>);
92   * 	String <jv>string</jv> = <jv>list</jv>.getString(1); <jc>// Returns "2" </jc>
93   * 	<jv>string</jv> = <jv>list</jv>.get(String.<jk>class</jk>, 1);  <jc>// Equivalent</jc>
94   *
95   * 	<jc>// Get one of the entries in the list as a bean (converted to a bean if it isn't already one)</jc>
96   * 	<jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[{name:'John Smith',age:45}]"</js>);
97   * 	Person <jv>person</jv> = <jv>list</jv>.get(Person.<jk>class</jk>, 0);
98   *
99   * 	<jc>// Iterate over a list of beans using the elements() method</jc>
100  * 	<jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[{name:'John Smith',age:45}]"</js>);
101  * 	<jk>for</jk> (Person <jv>person</jv> : <jv>list</jv>.elements(Person.<jk>class</jk>) {
102  * 		<jc>// Do something with p</jc>
103  * 	}
104  * </p>
105  *
106  * <h5 class='section'>Notes:</h5><ul>
107  * 	<li class='warn'>This class is not thread safe.
108  * </ul>
109  *
110  *
111  * @serial exclude
112  */
113 public class JsonList extends LinkedList<Object> {
114 	private static class UnmodifiableJsonList extends JsonList {
115 		private static final long serialVersionUID = 1L;
116 
117 		@SuppressWarnings("synthetic-access")
118 		UnmodifiableJsonList(JsonList contents) {
119 			if (nn(contents))
120 				contents.forEach(super::add);
121 		}
122 
123 		@Override /* Overridden from List */
124 		public void add(int location, Object object) {
125 			throw unsupportedOpReadOnly();
126 		}
127 
128 		@Override
129 		public boolean isUnmodifiable() { return true; }
130 
131 		@Override /* Overridden from List */
132 		public Object remove(int location) {
133 			throw unsupportedOpReadOnly();
134 		}
135 
136 		@Override /* Overridden from List */
137 		public Object set(int location, Object object) {
138 			throw unsupportedOpReadOnly();
139 		}
140 	}
141 
142 	private static final long serialVersionUID = 1L;
143 	/**
144 	 * An empty read-only JsonList.
145 	 *
146 	 * @serial exclude
147 	 */
148 	public static final JsonList EMPTY_LIST = new JsonList() {
149 		private static final long serialVersionUID = 1L;
150 
151 		@Override /* Overridden from List */
152 		public void add(int location, Object object) {
153 			throw unsupportedOpReadOnly();
154 		}
155 
156 		@Override /* Overridden from List */
157 		public ListIterator<Object> listIterator(int location) {
158 			return Collections.emptyList().listIterator(location);
159 		}
160 
161 		@Override /* Overridden from List */
162 		public Object remove(int location) {
163 			throw unsupportedOpReadOnly();
164 		}
165 
166 		@Override /* Overridden from List */
167 		public Object set(int location, Object object) {
168 			throw unsupportedOpReadOnly();
169 		}
170 
171 		@Override /* Overridden from List */
172 		public List<Object> subList(int start, int end) {
173 			return Collections.emptyList().subList(start, end);
174 		}
175 	};
176 
177 	/**
178 	 * Construct an empty list.
179 	 *
180 	 * @return An empty list.
181 	 */
182 	public static JsonList create() {
183 		return new JsonList();
184 	}
185 
186 	/**
187 	 * Construct a list initialized with the specified list.
188 	 *
189 	 * @param values
190 	 * 	The list to copy.
191 	 * 	<br>Can be <jk>null</jk>.
192 	 * @return A new list or <jk>null</jk> if the list was <jk>null</jk>.
193 	 */
194 	public static JsonList of(Collection<?> values) {
195 		return values == null ? null : new JsonList(values);
196 	}
197 
198 	/**
199 	 * Construct a list initialized with the specified values.
200 	 *
201 	 * @param values The values to add to this list.
202 	 * @return A new list, never <jk>null</jk>.
203 	 */
204 	public static JsonList of(Object...values) {
205 		return new JsonList(values);
206 	}
207 
208 	/**
209 	 * Convenience method for creating a list of array objects.
210 	 *
211 	 * @param values The initial values.
212 	 * @return A new list.
213 	 */
214 	public static JsonList ofArrays(Object[]...values) {
215 		var l = new JsonList();
216 		for (var v : values)
217 			l.add(v);
218 		return l;
219 	}
220 
221 	/**
222 	 * Convenience method for creating a list of collection objects.
223 	 *
224 	 * @param values The initial values.
225 	 * @return A new list.
226 	 */
227 	public static JsonList ofCollections(Collection<?>...values) {
228 		var l = new JsonList();
229 		for (var v : values)
230 			l.add(v);
231 		return l;
232 	}
233 
234 	/**
235 	 * Construct a list initialized with the specified JSON string.
236 	 *
237 	 * @param json
238 	 * 	The JSON text to parse.
239 	 * 	<br>Can be normal or simplified JSON.
240 	 * @return A new list or <jk>null</jk> if the string was null.
241 	 * @throws ParseException Malformed input encountered.
242 	 */
243 	public static JsonList ofJson(CharSequence json) throws ParseException {
244 		return json == null ? null : new JsonList(json);
245 	}
246 
247 	/**
248 	 * Construct a list initialized with the specified reader containing JSON.
249 	 *
250 	 * @param json
251 	 * 	The reader containing JSON text to parse.
252 	 * 	<br>Can contain normal or simplified JSON.
253 	 * @return A new list or <jk>null</jk> if the input was <jk>null</jk>.
254 	 * @throws ParseException Malformed input encountered.
255 	 */
256 	public static JsonList ofJson(Reader json) throws ParseException {
257 		return json == null ? null : new JsonList(json);
258 	}
259 
260 	/**
261 	 * Parses a string that can consist of either a JSON array or comma-delimited list.
262 	 *
263 	 * <p>
264 	 * The type of string is auto-detected.
265 	 *
266 	 * @param s The string to parse.
267 	 * @return The parsed string.
268 	 * @throws ParseException Malformed input encountered.
269 	 */
270 	public static JsonList ofJsonOrCdl(String s) throws ParseException {
271 		if (Utils.e(s))  // NOAI
272 			return null;
273 		if (! isProbablyJsonArray(s, true))
274 			return new JsonList((Object[])splita(s.trim(), ','));
275 		return new JsonList(s);
276 	}
277 
278 	/**
279 	 * Construct a list initialized with the specified string.
280 	 *
281 	 * @param in
282 	 * 	The input being parsed.
283 	 * 	<br>Can be <jk>null</jk>.
284 	 * @param p
285 	 * 	The parser to use to parse the input.
286 	 * 	<br>If <jk>null</jk>, uses {@link JsonParser}.
287 	 * @return A new list or <jk>null</jk> if the input was <jk>null</jk>.
288 	 * @throws ParseException Malformed input encountered.
289 	 */
290 	public static JsonList ofText(CharSequence in, Parser p) throws ParseException {
291 		return in == null ? null : new JsonList(in, p);
292 	}
293 
294 	/**
295 	 * Construct a list initialized with the specified string.
296 	 *
297 	 * @param in
298 	 * 	The reader containing the input being parsed.
299 	 * 	<br>Can contain normal or simplified JSON.
300 	 * @param p
301 	 * 	The parser to use to parse the input.
302 	 * 	<br>If <jk>null</jk>, uses {@link JsonParser}.
303 	 * @return A new list or <jk>null</jk> if the input was <jk>null</jk>.
304 	 * @throws ParseException Malformed input encountered.
305 	 */
306 	public static JsonList ofText(Reader in, Parser p) throws ParseException {
307 		return in == null ? null : new JsonList(in);
308 	}
309 
310 	transient BeanSession session = null;
311 
312 	private transient ObjectRest objectRest;
313 
314 	/**
315 	 * Construct an empty list.
316 	 */
317 	public JsonList() {}
318 
319 	/**
320 	 * Construct an empty list with the specified bean context.
321 	 *
322 	 * @param session The bean session to use for creating beans.
323 	 */
324 	public JsonList(BeanSession session) {
325 		this.session = session;
326 	}
327 
328 	/**
329 	 * Construct a list initialized with the specified JSON.
330 	 *
331 	 * @param json
332 	 * 	The JSON text to parse.
333 	 * 	<br>Can be normal or simplified JSON.
334 	 * @throws ParseException Malformed input encountered.
335 	 */
336 	public JsonList(CharSequence json) throws ParseException {
337 		this(json, JsonParser.DEFAULT);
338 	}
339 
340 	/**
341 	 * Construct a list initialized with the specified string.
342 	 *
343 	 * @param in
344 	 * 	The input being parsed.
345 	 * 	<br>Can be <jk>null</jk>.
346 	 * @param p
347 	 * 	The parser to use to parse the input.
348 	 * 	<br>If <jk>null</jk>, uses {@link JsonParser}.
349 	 * @throws ParseException Malformed input encountered.
350 	 */
351 	public JsonList(CharSequence in, Parser p) throws ParseException {
352 		this(p == null ? BeanContext.DEFAULT_SESSION : p.getBeanContext().getSession());
353 		if (p == null)
354 			p = JsonParser.DEFAULT;
355 		if (nn(in))
356 			p.parseIntoCollection(in, this, bs().object());
357 	}
358 
359 	/**
360 	 * Construct a list initialized with the specified list.
361 	 *
362 	 * @param copyFrom
363 	 * 	The list to copy.
364 	 * 	<br>Can be <jk>null</jk>.
365 	 */
366 	public JsonList(Collection<?> copyFrom) {
367 		super(copyFrom);
368 	}
369 
370 	/**
371 	 * Construct a list initialized with the contents.
372 	 *
373 	 * @param entries The entries to add to this list.
374 	 */
375 	public JsonList(Object...entries) {
376 		Collections.addAll(this, entries);
377 	}
378 
379 	/**
380 	 * Construct a list initialized with the specified reader containing JSON.
381 	 *
382 	 * @param json
383 	 * 	The reader containing JSON text to parse.
384 	 * 	<br>Can contain normal or simplified JSON.
385 	 * @throws ParseException Malformed input encountered.
386 	 */
387 	public JsonList(Reader json) throws ParseException {
388 		parse(json, JsonParser.DEFAULT);
389 	}
390 
391 	/**
392 	 * Construct a list initialized with the specified string.
393 	 *
394 	 * @param in
395 	 * 	The reader containing the input being parsed.
396 	 * 	<br>Can contain normal or simplified JSON.
397 	 * @param p
398 	 * 	The parser to use to parse the input.
399 	 * 	<br>If <jk>null</jk>, uses {@link JsonParser}.
400 	 * @throws ParseException Malformed input encountered.
401 	 */
402 	public JsonList(Reader in, Parser p) throws ParseException {
403 		this(p == null ? BeanContext.DEFAULT_SESSION : p.getBeanContext().getSession());
404 		parse(in, p);
405 	}
406 
407 	/**
408 	 * Adds all the values in the specified collection to this list.
409 	 *
410 	 * @param values The values to add to this list.
411 	 * @return This object.
412 	 */
413 	public JsonList append(Collection<?> values) {
414 		if (nn(values))
415 			addAll(values);
416 		return this;
417 	}
418 
419 	/**
420 	 * Adds the value to this list.
421 	 *
422 	 * @param value The value to add to this list.
423 	 * @return This object.
424 	 */
425 	public JsonList append(Object value) {
426 		add(value);
427 		return this;
428 	}
429 
430 	/**
431 	 * Adds all the values in the specified array to this list.
432 	 *
433 	 * @param values The values to add to this list.
434 	 * @return This object.
435 	 */
436 	public JsonList append(Object...values) {
437 		Collections.addAll(this, values);
438 		return this;
439 	}
440 
441 	/**
442 	 * Adds an entry to this list if the boolean flag is <jk>true</jk>.
443 	 *
444 	 * @param flag The boolean flag.
445 	 * @param value The value to add.
446 	 * @return This object.
447 	 */
448 	public JsonList appendIf(boolean flag, Object value) {
449 		if (flag)
450 			append(value);
451 		return this;
452 	}
453 
454 	/**
455 	 * Add if predicate matches.
456 	 *
457 	 * @param <T> The type being tested.
458 	 * @param test The predicate to match against.
459 	 * @param value The value to add if the predicate matches.
460 	 * @return This object.
461 	 */
462 	public <T> JsonList appendIf(Predicate<T> test, T value) {
463 		return appendIf(test(test, value), value);
464 	}
465 
466 	/**
467 	 * Adds all the entries in the specified collection to this list in reverse order.
468 	 *
469 	 * @param values The collection to add to this list.
470 	 * @return This object.
471 	 */
472 	public JsonList appendReverse(List<?> values) {
473 		for (ListIterator<?> i = values.listIterator(values.size()); i.hasPrevious();)
474 			add(i.previous());
475 		return this;
476 	}
477 
478 	/**
479 	 * Adds the contents of the array to the list in reverse order.
480 	 *
481 	 * <p>
482 	 * i.e. add values from the array from end-to-start order to the end of the list.
483 	 *
484 	 * @param values The collection to add to this list.
485 	 * @return This object.
486 	 */
487 	public JsonList appendReverse(Object...values) {
488 		for (var i = values.length - 1; i >= 0; i--)
489 			add(values[i]);
490 		return this;
491 	}
492 
493 	/**
494 	 * A synonym for {@link #toString()}
495 	 *
496 	 * @return This object as a JSON string.
497 	 */
498 	public String asJson() {
499 		return toString();
500 	}
501 
502 	/**
503 	 * Serialize this array to Simplified JSON.
504 	 *
505 	 * @return This object as a serialized string.
506 	 */
507 	public String asString() {
508 		return Json5Serializer.DEFAULT.toString(this);
509 	}
510 
511 	/**
512 	 * Serialize this array to a string using the specified serializer.
513 	 *
514 	 * @param serializer The serializer to use to convert this object to a string.
515 	 * @return This object as a serialized string.
516 	 */
517 	public String asString(WriterSerializer serializer) {
518 		return serializer.toString(this);
519 	}
520 
521 	/**
522 	 * Converts this object into the specified class type.
523 	 *
524 	 * <p>
525 	 * This method performs a round-trip serialization and deserialization to convert the list into the target type.
526 	 *
527 	 * <h5 class='section'>Notes:</h5><ul>
528 	 * 	<li class='warn'>The current implementation uses a serialization round-trip which may be inefficient for
529 	 * 		frequent conversions of large objects. Consider caching results or using direct object conversion where possible.
530 	 * </ul>
531 	 *
532 	 * @param cm The class type to convert this object to.
533 	 * @return A converted object.
534 	 */
535 	public Object cast(ClassMeta<?> cm) {
536 		try {
537 			return JsonParser.DEFAULT.parse(Json5Serializer.DEFAULT.serialize(this), cm);
538 		} catch (ParseException | SerializeException e) {
539 			throw toRex(e);
540 		}
541 	}
542 
543 	/**
544 	 * Similar to {@link #remove(int) remove(int)},but the key is a slash-delimited path used to traverse entries in
545 	 * this POJO.
546 	 *
547 	 * <p>
548 	 * For example, the following code is equivalent:
549 	 * </p>
550 	 * <p class='bjava'>
551 	 * 	JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"..."</js>);
552 	 *
553 	 * 	<jc>// Long way</jc>
554 	 * 	<jv>list</jv>.getMap(0).getList(<js>"bar"</js>).delete(0);
555 	 *
556 	 * 	<jc>// Using this method</jc>
557 	 * 	<jv>list</jv>.deleteAt(<js>"0/bar/0"</js>);
558 	 * </p>
559 	 *
560 	 * <p>
561 	 * This method uses the {@link ObjectRest} class to perform the lookup, so the map can contain any of the various
562 	 * class types that the {@link ObjectRest} class supports (e.g. beans, collections, arrays).
563 	 *
564 	 * @param path The path to the entry.
565 	 * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
566 	 */
567 	public Object deleteAt(String path) {
568 		return getObjectRest().delete(path);
569 	}
570 
571 	/**
572 	 * Creates an {@link Iterable} with elements of the specified child type.
573 	 *
574 	 * <p>
575 	 * Attempts to convert the child objects to the correct type if they aren't already the correct type.
576 	 *
577 	 * <p>
578 	 * The <c>next()</c> method on the returned iterator may throw a {@link InvalidDataConversionException} if
579 	 * the next element cannot be converted to the specified type.
580 	 *
581 	 * <p>
582 	 * See {@link BeanSession#convertToType(Object, ClassMeta)} for a description of valid conversions.
583 	 *
584 	 * <h5 class='section'>Example:</h5>
585 	 * <p class='bjava'>
586 	 * 	<jc>// Iterate over a list of JsonMaps.</jc>
587 	 * 	JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[{foo:'bar'},{baz:123}]"</js>);
588 	 * 	<jk>for</jk> (JsonMap <jv>map</jv> : <jv>list</jv>.elements(JsonMap.<jk>class</jk>)) {
589 	 * 		<jc>// Do something with map.</jc>
590 	 * 	}
591 	 *
592 	 * 	<jc>// Iterate over a list of ints.</jc>
593 	 * 	JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[1,2,3]"</js>);
594 	 * 	<jk>for</jk> (Integer <jv>i</jv> : <jv>list</jv>.elements(Integer.<jk>class</jk>)) {
595 	 * 		<jc>// Do something with i.</jc>
596 	 * 	}
597 	 *
598 	 * 	<jc>// Iterate over a list of beans.</jc>
599 	 * 	<jc>// Automatically converts to beans.</jc>
600 	 * 	JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[{name:'John Smith',age:45}]"</js>);
601 	 * 	<jk>for</jk> (Person <jv>p</jv> : <jv>list</jv>.elements(Person.<jk>class</jk>)) {
602 	 * 		<jc>// Do something with p.</jc>
603 	 * 	}
604 	 * </p>
605 	 *
606 	 * @param <E> The child object type.
607 	 * @param childType The child object type.
608 	 * @return A new <c>Iterable</c> object over this list.
609 	 */
610 	public <E> Iterable<E> elements(Class<E> childType) {
611 		final Iterator<?> iterator = iterator();
612 		return () -> new Iterator<>() {
613 
614 			@Override /* Overridden from Iterator */
615 			public boolean hasNext() {
616 				return iterator.hasNext();
617 			}
618 
619 			@Override /* Overridden from Iterator */
620 			public E next() {
621 				return bs().convertToType(iterator.next(), childType);
622 			}
623 
624 			@Override /* Overridden from Iterator */
625 			public void remove() {
626 				iterator.remove();
627 			}
628 
629 		};
630 	}
631 
632 	/**
633 	 * Get the entry at the specified index, converted to the specified type.
634 	 *
635 	 * <p>
636 	 * This is the preferred get method for simple types.
637 	 *
638 	 * <h5 class='section'>Examples:</h5>
639 	 * <p class='bjava'>
640 	 * 	JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"..."</js>);
641 	 *
642 	 * 	<jc>// Value converted to a string.</jc>
643 	 * 	String <jv>string</jv> = <jv>list</jv>.get(1, String.<jk>class</jk>);
644 	 *
645 	 * 	<jc>// Value converted to a bean.</jc>
646 	 * 	MyBean <jv>bean</jv> = <jv>list</jv>.get(2, MyBean.<jk>class</jk>);
647 	 *
648 	 * 	<jc>// Value converted to a bean array.</jc>
649 	 * 	MyBean[] <jv>beanArray</jv> = <jv>list</jv>.get(3, MyBean[].<jk>class</jk>);
650 	 *
651 	 * 	<jc>// Value converted to a linked-list of objects.</jc>
652 	 * 	List <jv>list2</jv> = <jv>list</jv>.get(4, LinkedList.<jk>class</jk>);
653 	 *
654 	 * 	<jc>// Value converted to a map of object keys/values.</jc>
655 	 * 	Map <jv>map</jv> = <jv>list</jv>.get(5, TreeMap.<jk>class</jk>);
656 	 * </p>
657 	 *
658 	 * <p>
659 	 * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions.
660 	 *
661 	 * @param index The index into this list.
662 	 * @param type The type of object to convert the entry to.
663 	 * @param <T> The type of object to convert the entry to.
664 	 * @return The converted entry.
665 	 */
666 	public <T> T get(int index, Class<T> type) {
667 		return bs().convertToType(get(index), type);
668 	}
669 
670 	/**
671 	 * Get the entry at the specified index, converted to the specified type.
672 	 *
673 	 * <p>
674 	 * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps).
675 	 *
676 	 * <h5 class='section'>Examples:</h5>
677 	 * <p class='bjava'>
678 	 * 	JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"..."</js>);
679 	 *
680 	 * 	<jc>// Value converted to a linked-list of strings.</jc>
681 	 * 	List&lt;String&gt; <jv>list1</jv> = <jv>list</jv>.get(1, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
682 	 *
683 	 * 	<jc>// Value converted to a linked-list of beans.</jc>
684 	 * 	List&lt;MyBean&gt; <jv>list2</jv> = <jv>list</jv>.get(2, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>);
685 	 *
686 	 * 	<jc>// Value converted to a linked-list of linked-lists of strings.</jc>
687 	 * 	List&lt;List&lt;String&gt;&gt; <jv>list3</jv> = <jv>list</jv>.get(3, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
688 	 *
689 	 * 	<jc>// Value converted to a map of string keys/values.</jc>
690 	 * 	Map&lt;String,String&gt; <jv>map1</jv> = <jv>list</jv>.get(4, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
691 	 *
692 	 * 	<jc>// Value converted to a map containing string keys and values of lists containing beans.</jc>
693 	 * 	Map&lt;String,List&lt;MyBean&gt;&gt; <jv>map2</jv> = <jv>list</jv>.get(5, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
694 	 * </p>
695 	 *
696 	 * <p>
697 	 * <c>Collection</c> classes are assumed to be followed by zero or one objects indicating the element type.
698 	 *
699 	 * <p>
700 	 * <c>Map</c> classes are assumed to be followed by zero or two meta objects indicating the key and value types.
701 	 *
702 	 * <p>
703 	 * The array can be arbitrarily long to indicate arbitrarily complex data structures.
704 	 *
705 	 * <p>
706 	 * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions.
707 	 *
708 	 * @param index The index into this list.
709 	 * @param type The type of object to convert the entry to.
710 	 * @param args The type arguments of the type to convert the entry to.
711 	 * @param <T> The type of object to convert the entry to.
712 	 * @return The converted entry.
713 	 */
714 	public <T> T get(int index, Type type, Type...args) {
715 		return bs().convertToType(get(index), type, args);
716 	}
717 
718 	/**
719 	 * Same as {@link #get(int,Class) get(int,Class)}, but the key is a slash-delimited path used to traverse entries in
720 	 * this POJO.
721 	 *
722 	 * <p>
723 	 * For example, the following code is equivalent:
724 	 * </p>
725 	 * <p class='bjava'>
726 	 * 	JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"..."</js>);
727 	 *
728 	 * 	<jc>// Long way</jc>
729 	 * 	<jk>long</jk> <jv>long1</jv> = <jv>list</jv>.getMap(<js>"0"</js>).getLong(<js>"baz"</js>);
730 	 *
731 	 * 	<jc>// Using this method</jc>
732 	 * 	<jk>long</jk> <jv>long2</jv> = <jv>list</jv>.getAt(<js>"0/baz"</js>, <jk>long</jk>.<jk>class</jk>);
733 	 * </p>
734 	 *
735 	 * <p>
736 	 * This method uses the {@link ObjectRest} class to perform the lookup, so the map can contain any of the various
737 	 * class types that the {@link ObjectRest} class supports (e.g. beans, collections, arrays).
738 	 *
739 	 * @param path The path to the entry.
740 	 * @param type The class type.
741 	 *
742 	 * @param <T> The class type.
743 	 * @return The value, or <jk>null</jk> if the entry doesn't exist.
744 	 */
745 	public <T> T getAt(String path, Class<T> type) {
746 		return getObjectRest().get(path, type);
747 	}
748 
749 	/**
750 	 * Same as {@link #getAt(String,Class)}, but allows for conversion to complex maps and collections.
751 	 *
752 	 * @param path The path to the entry.
753 	 * @param type The class type.
754 	 * @param args The class parameter types.
755 	 *
756 	 * @param <T> The class type.
757 	 * @return The value, or <jk>null</jk> if the entry doesn't exist.
758 	 */
759 	public <T> T getAt(String path, Type type, Type...args) {
760 		return getObjectRest().get(path, type, args);
761 	}
762 
763 	/**
764 	 * Returns the {@link BeanSession} currently associated with this list.
765 	 *
766 	 * @return The {@link BeanSession} currently associated with this list.
767 	 */
768 	public BeanSession getBeanSession() { return session; }
769 
770 	/**
771 	 * Shortcut for calling <code>get(index, Boolean.<jk>class</jk>)</code>.
772 	 *
773 	 * @param index The index.
774 	 * @return The converted value.
775 	 * @throws InvalidDataConversionException If value cannot be converted.
776 	 */
777 	public Boolean getBoolean(int index) {
778 		return get(index, Boolean.class);
779 	}
780 
781 	/**
782 	 * Returns the {@link ClassMeta} of the class of the object at the specified index.
783 	 *
784 	 * @param index An index into this list, zero-based.
785 	 * @return The data type of the object at the specified index, or <jk>null</jk> if the value is null.
786 	 */
787 	public ClassMeta<?> getClassMeta(int index) {
788 		return bs().getClassMetaForObject(get(index));
789 	}
790 
791 	/**
792 	 * Shortcut for calling <code>get(index, Integer.<jk>class</jk>)</code>.
793 	 *
794 	 * @param index The index.
795 	 * @return The converted value.
796 	 * @throws InvalidDataConversionException If value cannot be converted.
797 	 */
798 	public Integer getInt(int index) {
799 		return get(index, Integer.class);
800 	}
801 
802 	/**
803 	 * Shortcut for calling <code>get(index, JsonList.<jk>class</jk>)</code>.
804 	 *
805 	 * @param index The index.
806 	 * @return The converted value.
807 	 * @throws InvalidDataConversionException If value cannot be converted.
808 	 */
809 	public JsonList getList(int index) {
810 		return get(index, JsonList.class);
811 	}
812 
813 	/**
814 	 * Same as {@link #getList(int)} except converts the elements to the specified types.
815 	 *
816 	 * @param <E> The element type.
817 	 * @param index The index.
818 	 * @param elementType The element type class.
819 	 * @return The converted value.
820 	 * @throws InvalidDataConversionException If value cannot be converted.
821 	 */
822 	public <E> List<E> getList(int index, Class<E> elementType) {
823 		return bs().convertToType(get(index), List.class, elementType);
824 	}
825 
826 	/**
827 	 * Shortcut for calling <code>get(index, Long.<jk>class</jk>)</code>.
828 	 *
829 	 * @param index The index.
830 	 * @return The converted value.
831 	 * @throws InvalidDataConversionException If value cannot be converted.
832 	 */
833 	public Long getLong(int index) {
834 		return get(index, Long.class);
835 	}
836 
837 	/**
838 	 * Shortcut for calling <code>get(index, JsonMap.<jk>class</jk>)</code>.
839 	 *
840 	 * @param index The index.
841 	 * @return The converted value.
842 	 * @throws InvalidDataConversionException If value cannot be converted.
843 	 */
844 	public JsonMap getMap(int index) {
845 		return get(index, JsonMap.class);
846 	}
847 
848 	/**
849 	 * Same as {@link #getMap(int)} except converts the keys and values to the specified types.
850 	 *
851 	 * @param <K> The key type class.
852 	 * @param <V> The value type class.
853 	 * @param index The index.
854 	 * @param keyType The key type class.
855 	 * @param valType The value type class.
856 	 * @return The converted value.
857 	 * @throws InvalidDataConversionException If value cannot be converted.
858 	 */
859 	public <K,V> Map<K,V> getMap(int index, Class<K> keyType, Class<V> valType) {
860 		return bs().convertToType(get(index), Map.class, keyType, valType);
861 	}
862 
863 	/**
864 	 * Shortcut for calling <code>get(index, String.<jk>class</jk>)</code>.
865 	 *
866 	 * @param index The index.
867 	 * @return The converted value.
868 	 */
869 	public String getString(int index) {
870 		return get(index, String.class);
871 	}
872 
873 	/**
874 	 * Returns <jk>true</jk> if this list is unmodifiable.
875 	 *
876 	 * @return <jk>true</jk> if this list is unmodifiable.
877 	 */
878 	public boolean isUnmodifiable() { return false; }
879 
880 	/**
881 	 * Returns a modifiable copy of this list if it's unmodifiable.
882 	 *
883 	 * @return A modifiable copy of this list if it's unmodifiable, or this list if it is already modifiable.
884 	 */
885 	public JsonList modifiable() {
886 		if (isUnmodifiable())
887 			return new JsonList(this);
888 		return this;
889 	}
890 
891 	/**
892 	 * Similar to {@link #putAt(String,Object) putAt(String,Object)}, but used to append to collections and arrays.
893 	 *
894 	 * <p>
895 	 * For example, the following code is equivalent:
896 	 * </p>
897 	 * <p class='bjava'>
898 	 * 	JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"..."</js>);
899 	 *
900 	 * 	<jc>// Long way</jc>
901 	 * 	<jv>list</jv>.getMap(0).getList(<js>"bar"</js>).append(123);
902 	 *
903 	 * 	<jc>// Using this method</jc>
904 	 * 	<jv>list</jv>.postAt(<js>"0/bar"</js>, 123);
905 	 * </p>
906 	 *
907 	 * <p>
908 	 * This method uses the {@link ObjectRest} class to perform the lookup, so the map can contain any of the various
909 	 * class types that the {@link ObjectRest} class supports (e.g. beans, collections, arrays).
910 	 *
911 	 * @param path The path to the entry.
912 	 * @param o The new value.
913 	 * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
914 	 */
915 	public Object postAt(String path, Object o) {
916 		return getObjectRest().post(path, o);
917 	}
918 
919 	/**
920 	 * Same as {@link #set(int,Object) set(int,Object)}, but the key is a slash-delimited path used to traverse entries
921 	 * in this POJO.
922 	 *
923 	 * <p>
924 	 * For example, the following code is equivalent:
925 	 * </p>
926 	 * <p class='bjava'>
927 	 * 	JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"..."</js>);
928 	 *
929 	 * 	<jc>// Long way</jc>
930 	 * 	<jv>list</jv>.getMap(<js>"0"</js>).put(<js>"baz"</js>, 123);
931 	 *
932 	 * 	<jc>// Using this method</jc>
933 	 * 	<jv>list</jv>.putAt(<js>"0/baz"</js>, 123);
934 	 * </p>
935 	 *
936 	 * <p>
937 	 * This method uses the {@link ObjectRest} class to perform the lookup, so the map can contain any of the various
938 	 * class types that the {@link ObjectRest} class supports (e.g. beans, collections, arrays).
939 	 *
940 	 * @param path The path to the entry.
941 	 * @param o The new value.
942 	 * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
943 	 */
944 	public Object putAt(String path, Object o) {
945 		return getObjectRest().put(path, o);
946 	}
947 
948 	/**
949 	 * Override the default bean session used for converting POJOs.
950 	 *
951 	 * <p>
952 	 * Default is {@link BeanContext#DEFAULT}, which is sufficient in most cases.
953 	 *
954 	 * <p>
955 	 * Useful if you're serializing/parsing beans with transforms defined.
956 	 *
957 	 * @param session The new bean session.
958 	 * @return This object.
959 	 */
960 	public JsonList session(BeanSession session) {
961 		this.session = session;
962 		return this;
963 	}
964 
965 	/**
966 	 * Sets the {@link BeanSession} currently associated with this list.
967 	 *
968 	 * @param value The {@link BeanSession} currently associated with this list.
969 	 * @return This object.
970 	 */
971 	public JsonList setBeanSession(BeanSession value) {
972 		session = value;
973 		return this;
974 	}
975 
976 	@Override /* Overridden from Object */
977 	public String toString() {
978 		return Json5.of(this);
979 	}
980 
981 	/**
982 	 * Returns an unmodifiable copy of this list if it's modifiable.
983 	 *
984 	 * @return An unmodifiable copy of this list if it's modifiable, or this list if it is already unmodifiable.
985 	 */
986 	public JsonList unmodifiable() {
987 		if (this instanceof UnmodifiableJsonList this2)
988 			return this2;
989 		return new UnmodifiableJsonList(this);
990 	}
991 
992 	/**
993 	 * Convenience method for serializing this JsonList to the specified Writer using the JsonSerializer.DEFAULT
994 	 * serializer.
995 	 *
996 	 * @param w The writer to send the serialized contents of this object.
997 	 * @return This object.
998 	 * @throws IOException If a problem occurred trying to write to the writer.
999 	 * @throws SerializeException If a problem occurred trying to convert the output.
1000 	 */
1001 	public JsonList writeTo(Writer w) throws IOException, SerializeException {
1002 		JsonSerializer.DEFAULT.serialize(this, w);
1003 		return this;
1004 	}
1005 
1006 	private ObjectRest getObjectRest() {
1007 		if (objectRest == null)
1008 			objectRest = new ObjectRest(this);
1009 		return objectRest;
1010 	}
1011 
1012 	private void parse(Reader r, Parser p) throws ParseException {
1013 		if (p == null)
1014 			p = JsonParser.DEFAULT;
1015 		p.parseIntoCollection(r, this, bs().object());
1016 	}
1017 
1018 	BeanSession bs() {
1019 		if (session == null)
1020 			session = BeanContext.DEFAULT_SESSION;
1021 		return session;
1022 	}
1023 }