001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau; 014 015import java.io.*; 016import java.lang.reflect.*; 017import java.util.*; 018 019import org.apache.juneau.json.*; 020import org.apache.juneau.parser.*; 021import org.apache.juneau.serializer.*; 022import org.apache.juneau.utils.*; 023 024/** 025 * Java implementation of a JSON array. 026 * 027 * <p> 028 * An extension of {@link LinkedList}, so all methods available to in that class are also available to this class. 029 * 030 * <p> 031 * Note that the use of this class is optional. 032 * The serializers will accept any objects that implement the {@link Collection} interface. 033 * But this class provides some useful additional functionality when working with JSON models constructed from Java 034 * Collections Framework objects. 035 * For example, a constructor is provided for converting a JSON array string directly into a {@link List}. 036 * It also contains accessor methods for to avoid common typecasting when accessing elements in a list. 037 * 038 * <h5 class='section'>Example:</h5> 039 * <p class='bcode'> 040 * <jc>// Construct an empty List</jc> 041 * List l = <jk>new</jk> ObjectList(); 042 * 043 * <jc>// Construct a list of objects using various methods</jc> 044 * l = <jk>new</jk> ObjectList().append(<js>"foo"</js>).append(123).append(<jk>true</jk>); 045 * l = <jk>new</jk> ObjectList().append(<js>"foo"</js>, 123, <jk>true</jk>); <jc>// Equivalent</jc> 046 * l = <jk>new</jk> ObjectList(<js>"foo"</js>, 123, <jk>true</jk>); <jc>// Equivalent</jc> 047 * 048 * <jc>// Construct a list of integers from JSON</jc> 049 * l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); 050 * 051 * <jc>// Construct a list of generic ObjectMap objects from JSON</jc> 052 * l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:'bing'}]"</js>); 053 * 054 * <jc>// Construct a list of integers from XML</jc> 055 * String xml = <js>"<array><number>1</number><number>2</number><number>3</number></array>"</js>; 056 * l = <jk>new</jk> ObjectList(xml, DataFormat.<jsf>XML</jsf>); 057 * l = (List)XmlParser.<jsf>DEFAULT</jsf>.parse(xml); <jc>// Equivalent</jc> 058 * l = (List)XmlParser.<jsf>DEFAULT</jsf>.parse(Object.<jk>class</jk>, xml); <jc>// Equivalent</jc> 059 * l = XmlParser.<jsf>DEFAULT</jsf>.parse(List.<jk>class</jk>, xml); <jc>// Equivalent</jc> 060 * l = XmlParser.<jsf>DEFAULT</jsf>.parse(ObjectList.<jk>class</jk>, xml); <jc>// Equivalent</jc> 061 * 062 * <jc>// Construct JSON from ObjectList</jc> 063 * l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:'bing'}]"</js>); 064 * String json = l.toString(); <jc>// Produces "[{foo:'bar'},{baz:'bing'}]"</jc> 065 * json = l.toString(JsonSerializer.<jsf>DEFAULT_CONDENSED</jsf>); <jc>// Equivalent</jc> 066 * json = JsonSerializer.<jsf>DEFAULT_CONDENSED</jsf>.serialize(l); <jc>// Equivalent</jc> 067 * 068 * <jc>// Get one of the entries in the list as an Integer</jc> 069 * l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); 070 * Integer i = l.getInt(1); 071 * i = l.get(Integer.<jk>class</jk>, 1); <jc>// Equivalent</jc> 072 * 073 * <jc>// Get one of the entries in the list as an Float</jc> 074 * l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); 075 * Float f = l.getFloat(1); <jc>// Returns 2f </jc> 076 * f = l.get(Float.<jk>class</jk>, 1); <jc>// Equivalent</jc> 077 * 078 * <jc>// Same as above, except converted to a String</jc> 079 * l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); 080 * String s = l.getString(1); <jc>// Returns "2" </jc> 081 * s = l.get(String.<jk>class</jk>, 1); <jc>// Equivalent</jc> 082 * 083 * <jc>// Get one of the entries in the list as a bean (converted to a bean if it isn't already one)</jc> 084 * l = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>); 085 * Person p = l.get(Person.<jk>class</jk>, 0); 086 * 087 * <jc>// Iterate over a list of beans using the elements() method</jc> 088 * ObjectList ObjectList = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>); 089 * <jk>for</jk> (Person p : ObjectList.elements(Person.<jk>class</jk>) { 090 * <jc>// Do something with p</jc> 091 * } 092 * </p> 093 * 094 * <p> 095 * This class is not thread safe. 096 */ 097public class ObjectList extends LinkedList<Object> { 098 private static final long serialVersionUID = 1L; 099 100 transient BeanSession session = null; 101 private transient PojoRest pojoRest; 102 103 /** 104 * An empty read-only ObjectList. 105 */ 106 public static final ObjectList EMPTY_LIST = new ObjectList() { 107 private static final long serialVersionUID = 1L; 108 109 @Override /* List */ 110 public void add(int location, Object object) { 111 throw new UnsupportedOperationException(); 112 } 113 114 @Override /* List */ 115 public ListIterator<Object> listIterator(final int location) { 116 return Collections.emptyList().listIterator(location); 117 } 118 119 @Override /* List */ 120 public Object remove(int location) { 121 throw new UnsupportedOperationException(); 122 } 123 124 @Override /* List */ 125 public Object set(int location, Object object) { 126 throw new UnsupportedOperationException(); 127 } 128 129 @Override /* List */ 130 public List<Object> subList(int start, int end) { 131 return Collections.emptyList().subList(start, end); 132 } 133 }; 134 135 /** 136 * Construct a JSON array directly from text using the specified parser. 137 * 138 * @param s The string being parsed. 139 * @param p The parser to use to parse the input. 140 * @throws ParseException If the input contains a syntax error or is malformed. 141 */ 142 public ObjectList(CharSequence s, Parser p) throws ParseException { 143 this(p == null ? BeanContext.DEFAULT.createSession() : p.createBeanSession()); 144 if (p == null) 145 p = JsonParser.DEFAULT; 146 try { 147 if (s != null) 148 p.parseIntoCollection(s, this, session.object()); 149 } catch (ParseException e) { 150 throw new ParseException("Invalid input for {0} parser.\n---start---\n{1}\n---end---", 151 p.getClass().getSimpleName(), s).initCause(e); 152 } 153 } 154 155 /** 156 * Shortcut for <code><jk>new</jk> ObjectList(String,JsonParser.<jsf>DEFAULT</jsf>);</code> 157 * 158 * @param s The string being parsed. 159 * @throws ParseException If the input contains a syntax error or is malformed. 160 */ 161 public ObjectList(CharSequence s) throws ParseException { 162 this(s, null); 163 } 164 165 /** 166 * Construct a JSON array directly from a reader using the specified parser. 167 * 168 * @param r 169 * The reader to read from. 170 * Will automatically be wrapped in a {@link BufferedReader} if it isn't already a BufferedReader. 171 * @param p The parser to use to parse the input. 172 * @throws ParseException If the input contains a syntax error or is malformed. 173 * @throws IOException If a problem occurred trying to read from the reader. 174 */ 175 public ObjectList(Reader r, Parser p) throws ParseException, IOException { 176 this(p == null ? BeanContext.DEFAULT.createSession() : p.createBeanSession()); 177 parseReader(r, p); 178 } 179 180 /** 181 * Shortcut for <code><jk>new</jk> ObjectList(reader, JsonParser.<jsf>DEFAULT</jsf>)</code>. 182 * 183 * @param r 184 * The reader to read from. 185 * The reader will be wrapped in a {@link BufferedReader} if it isn't already. 186 * @throws ParseException If the input contains a syntax error or is malformed. 187 * @throws IOException If a problem occurred trying to read from the reader. 188 */ 189 public ObjectList(Reader r) throws ParseException, IOException { 190 this(BeanContext.DEFAULT.createSession()); 191 parseReader(r, JsonParser.DEFAULT); 192 } 193 194 private void parseReader(Reader r, Parser p) throws ParseException { 195 if (p == null) 196 p = JsonParser.DEFAULT; 197 p.parseIntoCollection(r, this, session.object()); 198 } 199 200 /** 201 * Construct an empty JSON array. (i.e. an empty {@link LinkedList}). 202 */ 203 public ObjectList() { 204 this(BeanContext.DEFAULT.createSession()); 205 } 206 207 /** 208 * Construct an empty JSON array with the specified bean context. (i.e. an empty {@link LinkedList}). 209 * 210 * @param session The bean context to associate with this object list for creating beans. 211 */ 212 public ObjectList(BeanSession session) { 213 super(); 214 this.session = session; 215 } 216 217 /** 218 * Construct a JSON array and fill it with the specified objects. 219 * 220 * @param o A list of objects to add to this list. 221 */ 222 public ObjectList(Object... o) { 223 super(Arrays.asList(o)); 224 } 225 226 /** 227 * Construct a JSON array and fill it with the specified collection of objects. 228 * 229 * @param c A list of objects to add to this list. 230 */ 231 public ObjectList(Collection<?> c) { 232 super(c); 233 } 234 235 /** 236 * Override the default bean session used for converting POJOs. 237 * 238 * <p> 239 * Default is {@link BeanContext#DEFAULT}, which is sufficient in most cases. 240 * 241 * <p> 242 * Useful if you're serializing/parsing beans with transforms defined. 243 * 244 * @param session The new bean session. 245 * @return This object (for method chaining). 246 */ 247 public ObjectList setBeanSession(BeanSession session) { 248 this.session = session; 249 return this; 250 } 251 252 /** 253 * Convenience method for adding multiple objects to this list. 254 * 255 * @param o The objects to add to the list. 256 * @return This object (for method chaining). 257 */ 258 public ObjectList append(Object...o) { 259 for (Object o2 : o) 260 add(o2); 261 return this; 262 } 263 264 /** 265 * Get the entry at the specified index, converted to the specified type. 266 * 267 * <p> 268 * This is the preferred get method for simple types. 269 * 270 * <h5 class='section'>Examples:</h5> 271 * <p class='bcode'> 272 * ObjectList l = <jk>new</jk> ObjectList(<js>"..."</js>); 273 * 274 * <jc>// Value converted to a string.</jc> 275 * String s = l.get(1, String.<jk>class</jk>); 276 * 277 * <jc>// Value converted to a bean.</jc> 278 * MyBean b = l.get(2, MyBean.<jk>class</jk>); 279 * 280 * <jc>// Value converted to a bean array.</jc> 281 * MyBean[] ba = l.get(3, MyBean[].<jk>class</jk>); 282 * 283 * <jc>// Value converted to a linked-list of objects.</jc> 284 * List l1 = l.get(4, LinkedList.<jk>class</jk>); 285 * 286 * <jc>// Value converted to a map of object keys/values.</jc> 287 * Map m1 = l.get(5, TreeMap.<jk>class</jk>); 288 * </p> 289 * 290 * <p> 291 * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions. 292 * 293 * @param index The index into this list. 294 * @param type The type of object to convert the entry to. 295 * @param <T> The type of object to convert the entry to. 296 * @return The converted entry. 297 */ 298 public <T> T get(int index, Class<T> type) { 299 return session.convertToType(get(index), type); 300 } 301 302 /** 303 * Get the entry at the specified index, converted to the specified type. 304 * 305 * <p> 306 * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). 307 * 308 * <h5 class='section'>Examples:</h5> 309 * <p class='bcode'> 310 * ObjectList l = <jk>new</jk> ObjectList(<js>"..."</js>); 311 * 312 * <jc>// Value converted to a linked-list of strings.</jc> 313 * List<String> l1 = l.get(1, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 314 * 315 * <jc>// Value converted to a linked-list of beans.</jc> 316 * List<MyBean> l2 = l.get(2, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 317 * 318 * <jc>// Value converted to a linked-list of linked-lists of strings.</jc> 319 * List<List<String>> l3 = l.get(3, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 320 * 321 * <jc>// Value converted to a map of string keys/values.</jc> 322 * Map<String,String> m1 = l.get(4, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 323 * 324 * <jc>// Value converted to a map containing string keys and values of lists containing beans.</jc> 325 * Map<String,List<MyBean>> m2 = l.get(5, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 326 * </p> 327 * 328 * <p> 329 * <code>Collection</code> classes are assumed to be followed by zero or one objects indicating the element type. 330 * 331 * <p> 332 * <code>Map</code> classes are assumed to be followed by zero or two meta objects indicating the key and value types. 333 * 334 * <p> 335 * The array can be arbitrarily long to indicate arbitrarily complex data structures. 336 * 337 * <p> 338 * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions. 339 * 340 * @param index The index into this list. 341 * @param type The type of object to convert the entry to. 342 * @param args The type arguments of the type to convert the entry to. 343 * @param <T> The type of object to convert the entry to. 344 * @return The converted entry. 345 */ 346 public <T> T get(int index, Type type, Type...args) { 347 return session.convertToType(get(index), type, args); 348 } 349 350 /** 351 * Shortcut for calling <code>get(index, String.<jk>class</jk>)</code>. 352 * 353 * @param index The index. 354 * @return The converted value. 355 */ 356 public String getString(int index) { 357 return get(index, String.class); 358 } 359 360 /** 361 * Shortcut for calling <code>get(index, Integer.<jk>class</jk>)</code>. 362 * 363 * @param index The index. 364 * @return The converted value. 365 * @throws InvalidDataConversionException If value cannot be converted. 366 */ 367 public Integer getInt(int index) { 368 return get(index, Integer.class); 369 } 370 371 /** 372 * Shortcut for calling <code>get(index, Boolean.<jk>class</jk>)</code>. 373 * 374 * @param index The index. 375 * @return The converted value. 376 * @throws InvalidDataConversionException If value cannot be converted. 377 */ 378 public Boolean getBoolean(int index) { 379 return get(index, Boolean.class); 380 } 381 382 /** 383 * Shortcut for calling <code>get(index, Long.<jk>class</jk>)</code>. 384 * 385 * @param index The index. 386 * @return The converted value. 387 * @throws InvalidDataConversionException If value cannot be converted. 388 */ 389 public Long getLong(int index) { 390 return get(index, Long.class); 391 } 392 393 /** 394 * Shortcut for calling <code>get(index, Map.<jk>class</jk>)</code>. 395 * 396 * @param index The index. 397 * @return The converted value. 398 * @throws InvalidDataConversionException If value cannot be converted. 399 */ 400 public Map<?,?> getMap(int index) { 401 return get(index, Map.class); 402 } 403 404 /** 405 * Same as {@link #getMap(int)} except converts the keys and values to the specified types. 406 * 407 * @param index The index. 408 * @param keyType The key type class. 409 * @param valType The value type class. 410 * @return The converted value. 411 * @throws InvalidDataConversionException If value cannot be converted. 412 */ 413 public <K,V> Map<K,V> getMap(int index, Class<K> keyType, Class<V> valType) { 414 return session.convertToType(get(index), Map.class, keyType, valType); 415 } 416 417 /** 418 * Shortcut for calling <code>get(index, List.<jk>class</jk>)</code>. 419 * 420 * @param index The index. 421 * @return The converted value. 422 * @throws InvalidDataConversionException If value cannot be converted. 423 */ 424 public List<?> getList(int index) { 425 return get(index, List.class); 426 } 427 428 /** 429 * Same as {@link #getList(int)} except converts the elements to the specified types. 430 * 431 * @param index The index. 432 * @param elementType The element type class. 433 * @return The converted value. 434 * @throws InvalidDataConversionException If value cannot be converted. 435 */ 436 public <E> List<E> getList(int index, Class<E> elementType) { 437 return session.convertToType(get(index), List.class, elementType); 438 } 439 440 /** 441 * Shortcut for calling <code>get(index, ObjectMap.<jk>class</jk>)</code>. 442 * 443 * @param index The index. 444 * @return The converted value. 445 * @throws InvalidDataConversionException If value cannot be converted. 446 */ 447 public ObjectMap getObjectMap(int index) { 448 return get(index, ObjectMap.class); 449 } 450 451 /** 452 * Shortcut for calling <code>get(index, ObjectList.<jk>class</jk>)</code>. 453 * 454 * @param index The index. 455 * @return The converted value. 456 * @throws InvalidDataConversionException If value cannot be converted. 457 */ 458 public ObjectList getObjectList(int index) { 459 return get(index, ObjectList.class); 460 } 461 462 /** 463 * Same as {@link #get(int,Class) get(int,Class)}, but the key is a slash-delimited path used to traverse entries in 464 * this POJO. 465 * 466 * <p> 467 * For example, the following code is equivalent: 468 * </p> 469 * <p class='bcode'> 470 * ObjectMap m = getObjectMap(); 471 * 472 * <jc>// Long way</jc> 473 * <jk>long</jk> l = m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(<js>"0"</js>).getLong(<js>"baz"</js>); 474 * 475 * <jc>// Using this method</jc> 476 * <jk>long</jk> l = m.getAt(<js>"foo/bar/0/baz"</js>, <jk>long</jk>.<jk>class</jk>); 477 * </p> 478 * 479 * <p> 480 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 481 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 482 * 483 * @param path The path to the entry. 484 * @param type The class type. 485 * 486 * @param <T> The class type. 487 * @return The value, or <jk>null</jk> if the entry doesn't exist. 488 */ 489 public <T> T getAt(String path, Class<T> type) { 490 return getPojoRest().get(path, type); 491 } 492 493 /** 494 * Same as {@link #getAt(String,Class)}, but allows for conversion to complex maps and collections. 495 * 496 * @param path The path to the entry. 497 * @param type The class type. 498 * @param args The class parameter types. 499 * 500 * @param <T> The class type. 501 * @return The value, or <jk>null</jk> if the entry doesn't exist. 502 */ 503 public <T> T getAt(String path, Type type, Type...args) { 504 return getPojoRest().get(path, type, args); 505 } 506 507 /** 508 * Same as {@link #set(int,Object) set(int,Object)}, but the key is a slash-delimited path used to traverse entries 509 * in this POJO. 510 * 511 * <p> 512 * For example, the following code is equivalent: 513 * </p> 514 * <p class='bcode'> 515 * ObjectMap m = getObjectMap(); 516 * 517 * <jc>// Long way</jc> 518 * m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(<js>"0"</js>).put(<js>"baz"</js>, 123); 519 * 520 * <jc>// Using this method</jc> 521 * m.putAt(<js>"foo/bar/0/baz"</js>, 123); 522 * </p> 523 * 524 * <p> 525 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 526 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 527 * 528 * @param path The path to the entry. 529 * @param o The new value. 530 * @return The previous value, or <jk>null</jk> if the entry doesn't exist. 531 */ 532 public Object putAt(String path, Object o) { 533 return getPojoRest().put(path, o); 534 } 535 536 /** 537 * Similar to {@link #putAt(String,Object) putAt(String,Object)}, but used to append to collections and arrays. 538 * 539 * <p> 540 * For example, the following code is equivalent: 541 * </p> 542 * <p class='bcode'> 543 * ObjectMap m = getObjectMap(); 544 * 545 * <jc>// Long way</jc> 546 * m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).append(123); 547 * 548 * <jc>// Using this method</jc> 549 * m.postAt(<js>"foo/bar"</js>, 123); 550 * </p> 551 * 552 * <p> 553 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 554 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 555 * 556 * @param path The path to the entry. 557 * @param o The new value. 558 * @return The previous value, or <jk>null</jk> if the entry doesn't exist. 559 */ 560 public Object postAt(String path, Object o) { 561 return getPojoRest().post(path, o); 562 } 563 564 /** 565 * Similar to {@link #remove(int) remove(int)},but the key is a slash-delimited path used to traverse entries in 566 * this POJO. 567 * 568 * <p> 569 * For example, the following code is equivalent: 570 * </p> 571 * <p class='bcode'> 572 * ObjectMap m = getObjectMap(); 573 * 574 * <jc>// Long way</jc> 575 * m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(1).remove(<js>"baz"</js>); 576 * 577 * <jc>// Using this method</jc> 578 * m.deleteAt(<js>"foo/bar/0/baz"</js>); 579 * </p> 580 * 581 * <p> 582 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 583 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 584 * 585 * @param path The path to the entry. 586 * @return The previous value, or <jk>null</jk> if the entry doesn't exist. 587 */ 588 public Object deleteAt(String path) { 589 return getPojoRest().delete(path); 590 } 591 592 /** 593 * Creates an {@link Iterable} with elements of the specified child type. 594 * 595 * <p> 596 * Attempts to convert the child objects to the correct type if they aren't already the correct type. 597 * 598 * <p> 599 * The <code>next()</code> method on the returned iterator may throw a {@link InvalidDataConversionException} if 600 * the next element cannot be converted to the specified type. 601 * 602 * <p> 603 * See {@link BeanSession#convertToType(Object, ClassMeta)} for a description of valid conversions. 604 * 605 * <h5 class='section'>Example:</h5> 606 * <p class='bcode'> 607 * <jc>// Iterate over a list of ObjectMaps.</jc> 608 * ObjectList l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:123}]"</js>); 609 * for (ObjectMap m : l.elements(ObjectMap.<jk>class</jk>)) { 610 * <jc>// Do something with m.</jc> 611 * } 612 * 613 * <jc>// Iterate over a list of ints.</jc> 614 * ObjectList l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); 615 * for (Integer i : l.elements(Integer.<jk>class</jk>)) { 616 * <jc>// Do something with i.</jc> 617 * } 618 * 619 * <jc>// Iterate over a list of beans.</jc> 620 * <jc>// Automatically converts to beans.</jc> 621 * ObjectList l = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>); 622 * for (Person p : l.elements(Person.<jk>class</jk>)) { 623 * <jc>// Do something with p.</jc> 624 * } 625 * </p> 626 * 627 * @param <E> The child object type. 628 * @param childType The child object type. 629 * @return A new <code>Iterable</code> object over this list. 630 */ 631 public <E> Iterable<E> elements(final Class<E> childType) { 632 final Iterator<?> i = iterator(); 633 return new Iterable<E>() { 634 635 @Override /* Iterable */ 636 public Iterator<E> iterator() { 637 return new Iterator<E>() { 638 639 @Override /* Iterator */ 640 public boolean hasNext() { 641 return i.hasNext(); 642 } 643 644 @Override /* Iterator */ 645 public E next() { 646 return session.convertToType(i.next(), childType); 647 } 648 649 @Override /* Iterator */ 650 public void remove() { 651 i.remove(); 652 } 653 654 }; 655 } 656 }; 657 } 658 659 /** 660 * Returns the {@link ClassMeta} of the class of the object at the specified index. 661 * 662 * @param index An index into this list, zero-based. 663 * @return The data type of the object at the specified index, or <jk>null</jk> if the value is null. 664 */ 665 public ClassMeta<?> getClassMeta(int index) { 666 return session.getClassMetaForObject(get(index)); 667 } 668 669 private PojoRest getPojoRest() { 670 if (pojoRest == null) 671 pojoRest = new PojoRest(this); 672 return pojoRest; 673 } 674 675 /** 676 * Serialize this array to a string using the specified serializer. 677 * 678 * @param serializer The serializer to use to convert this object to a string. 679 * @return This object as a serialized string. 680 * @throws SerializeException If a problem occurred trying to convert the output. 681 */ 682 public String toString(WriterSerializer serializer) throws SerializeException { 683 return serializer.serialize(this); 684 } 685 686 /** 687 * Serialize this array to JSON using the {@link JsonSerializer#DEFAULT} serializer. 688 */ 689 @Override /* Object */ 690 public String toString() { 691 try { 692 return this.toString(JsonSerializer.DEFAULT_LAX); 693 } catch (SerializeException e) { 694 return e.getLocalizedMessage(); 695 } 696 } 697 698 /** 699 * Convenience method for serializing this ObjectList to the specified Writer using the JsonSerializer.DEFAULT 700 * serializer. 701 * 702 * @param w The writer to send the serialized contents of this object. 703 * @throws IOException If a problem occurred trying to write to the writer. 704 * @throws SerializeException If a problem occurred trying to convert the output. 705 */ 706 public void serializeTo(Writer w) throws IOException, SerializeException { 707 JsonSerializer.DEFAULT.serialize(this); 708 } 709}