001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.marshall.collections; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020import static org.apache.juneau.commons.utils.StringUtils.*; 021import static org.apache.juneau.commons.utils.ThrowableUtils.*; 022 023import java.io.*; 024import java.util.*; 025import java.util.function.*; 026 027import org.apache.juneau.commons.utils.*; 028import org.apache.juneau.marshall.*; 029import org.apache.juneau.marshall.json.*; 030import org.apache.juneau.marshall.json5.*; 031import org.apache.juneau.marshall.marshaller.*; 032import org.apache.juneau.marshall.marshaller.Json; 033import org.apache.juneau.marshall.parser.*; 034import org.apache.juneau.marshall.serializer.*; 035 036/** 037 * Java implementation of a JSON array. 038 * 039 * <p> 040 * This class is a subclass of {@link MarshalledList} that carries strict-JSON-specific surface, including 041 * strict-JSON {@link #toString()} (via {@link Json#of(Object)}), {@link JsonParser}-defaulting 042 * {@link CharSequence} / {@link Reader} constructors, and convenience methods for serializing to the 043 * various JSON dialects (JSON5, JSON Lines, canonical JSON, HJSON). 044 * 045 * <p> 046 * <b>v10.0 behavioral break:</b> {@code toString()} now produces strict (RFC 8259) JSON (was JSON5), 047 * and the {@link CharSequence} / {@link Reader} constructors now default to {@link JsonParser#DEFAULT} 048 * (was {@link Json5Parser#DEFAULT}). For the previous JSON5 behavior use {@link Json5List}. 049 * 050 * <p> 051 * Note that the use of this class is optional for generating JSON. The serializers will accept any objects that implement the 052 * {@link Collection} interface. But this class provides some useful additional functionality when working with JSON 053 * models constructed from Java Collections Framework objects. For example, a constructor is provided for converting a 054 * JSON array string directly into a {@link List}. It also contains accessor methods for to avoid common typecasting 055 * when accessing elements in a list. 056 * 057 * <h5 class='section'>Example:</h5> 058 * <p class='bjava'> 059 * <jc>// Construct an empty List</jc> 060 * JsonList <jv>list</jv> = JsonList.<jsm>of</jsm>(); 061 * 062 * <jc>// Construct a list of objects using various methods</jc> 063 * <jv>list</jv> = JsonList.<jsm>of</jsm>().a(<js>"foo"</js>).a(123).a(<jk>true</jk>); 064 * <jv>list</jv> = JsonList.<jsm>of</jsm>().a(<js>"foo"</js>, 123, <jk>true</jk>); <jc>// Equivalent</jc> 065 * <jv>list</jv> = JsonList.<jsm>of</jsm>(<js>"foo"</js>, 123, <jk>true</jk>); <jc>// Equivalent</jc> 066 * 067 * <jc>// Construct a list of integers from JSON</jc> 068 * <jv>list</jv> = JsonList.<jsm>ofString</jsm>(<js>"[1,2,3]"</js>); 069 * 070 * <jc>// Construct a list of generic JsonMap objects from JSON</jc> 071 * <jv>list</jv> = JsonList.<jsm>ofString</jsm>(<js>"[{foo:'bar'},{baz:'bing'}]"</js>); 072 * 073 * <jc>// Construct JSON from JsonList</jc> 074 * <jv>list</jv> = JsonList.<jsm>ofString</jsm>(<js>"[{foo:'bar'},{baz:'bing'}]"</js>); 075 * String <jv>json</jv> = <jv>list</jv>.toString(); <jc>// Produces "[{foo:'bar'},{baz:'bing'}]"</jc> 076 * <jv>json</jv> = <jv>list</jv>.toString(JsonSerializer.<jsf>DEFAULT</jsf>); <jc>// Equivalent</jc> 077 * <jv>json</jv> = JsonSerializer.<jsf>DEFAULT</jsf>.write(<jv>list</jv>); <jc>// Equivalent</jc> 078 * 079 * <jc>// Get one of the entries in the list as an Integer</jc> 080 * <jv>list</jv> = JsonList.<jsm>ofString</jsm>(<js>"[1,2,3]"</js>); 081 * Integer <jv>integer</jv> = <jv>list</jv>.getInt(1); 082 * <jv>integer</jv> = <jv>list</jv>.get(Integer.<jk>class</jk>, 1); <jc>// Equivalent</jc> 083 * 084 * <jc>// Iterate over a list of beans using the elements() method</jc> 085 * <jv>list</jv> = JsonList.<jsm>ofString</jsm>(<js>"[{name:'John Smith',age:45}]"</js>); 086 * <jk>for</jk> (Person <jv>person</jv> : <jv>list</jv>.elements(Person.<jk>class</jk>) { 087 * <jc>// Do something with p</jc> 088 * } 089 * </p> 090 * 091 * <h5 class='section'>Notes:</h5><ul> 092 * <li class='warn'>This class is not thread safe. 093 * </ul> 094 * 095 * 096 * @serial exclude 097 */ 098@SuppressWarnings({ 099 "java:S110", // Class has many fields, acceptable for collection implementation 100 "java:S1206", // Inherits equals/hashCode from LinkedList; List equality is element-based 101 "java:S2160", // equals() inherited from LinkedList; element-based equality is correct for a JSON list 102 "java:S9149" // Per-type static factories intentionally shadow the parent's. 103}) 104public class JsonList extends MarshalledList { 105 private static class Unmodifiable extends JsonList { 106 private static final long serialVersionUID = 1L; 107 108 @SuppressWarnings({ 109 "synthetic-access" // Access to outer class members is intentional 110 }) 111 Unmodifiable(JsonList contents) { 112 if (nn(contents)) 113 contents.forEach(super::add); 114 } 115 116 @Override /* Overridden from List */ 117 public boolean add(Object object) { 118 throw uoroex(); 119 } 120 121 @Override /* Overridden from List */ 122 public void add(int location, Object object) { 123 throw uoroex(); 124 } 125 126 @Override /* Overridden from List */ 127 public boolean addAll(Collection<?> c) { 128 throw uoroex(); 129 } 130 131 @Override /* Overridden from List */ 132 public boolean addAll(int location, Collection<?> c) { 133 throw uoroex(); 134 } 135 136 @Override /* Overridden from Deque */ 137 public void addFirst(Object object) { 138 throw uoroex(); 139 } 140 141 @Override /* Overridden from Deque */ 142 public void addLast(Object object) { 143 throw uoroex(); 144 } 145 146 @Override /* Overridden from List */ 147 public void clear() { 148 throw uoroex(); 149 } 150 151 @Override 152 public boolean isUnmodifiable() { return true; } 153 154 @Override /* Overridden from List */ 155 public Iterator<Object> iterator() { 156 return readOnlyIterator(super.iterator()); 157 } 158 159 @Override /* Overridden from List */ 160 public ListIterator<Object> listIterator() { 161 return readOnlyListIterator(super.listIterator()); 162 } 163 164 @Override /* Overridden from List */ 165 public ListIterator<Object> listIterator(int location) { 166 return readOnlyListIterator(super.listIterator(location)); 167 } 168 169 @Override /* Overridden from Queue */ 170 public boolean offer(Object object) { 171 throw uoroex(); 172 } 173 174 @Override /* Overridden from Deque */ 175 public boolean offerFirst(Object object) { 176 throw uoroex(); 177 } 178 179 @Override /* Overridden from Deque */ 180 public boolean offerLast(Object object) { 181 throw uoroex(); 182 } 183 184 @Override /* Overridden from Queue */ 185 public Object poll() { 186 throw uoroex(); 187 } 188 189 @Override /* Overridden from Deque */ 190 public Object pollFirst() { 191 throw uoroex(); 192 } 193 194 @Override /* Overridden from Deque */ 195 public Object pollLast() { 196 throw uoroex(); 197 } 198 199 @Override /* Overridden from Deque */ 200 public Object pop() { 201 throw uoroex(); 202 } 203 204 @Override /* Overridden from Deque */ 205 public void push(Object object) { 206 throw uoroex(); 207 } 208 209 @Override /* Overridden from Queue */ 210 public Object remove() { 211 throw uoroex(); 212 } 213 214 @Override /* Overridden from List */ 215 public Object remove(int location) { 216 throw uoroex(); 217 } 218 219 @Override /* Overridden from List */ 220 public boolean remove(Object object) { 221 throw uoroex(); 222 } 223 224 @Override /* Overridden from List */ 225 public boolean removeAll(Collection<?> c) { 226 throw uoroex(); 227 } 228 229 @Override /* Overridden from Deque */ 230 public Object removeFirst() { 231 throw uoroex(); 232 } 233 234 @Override /* Overridden from Deque */ 235 public boolean removeFirstOccurrence(Object object) { 236 throw uoroex(); 237 } 238 239 @Override /* Overridden from Collection */ 240 public boolean removeIf(Predicate<? super Object> filter) { 241 throw uoroex(); 242 } 243 244 @Override /* Overridden from Deque */ 245 public Object removeLast() { 246 throw uoroex(); 247 } 248 249 @Override /* Overridden from Deque */ 250 public boolean removeLastOccurrence(Object object) { 251 throw uoroex(); 252 } 253 254 @Override /* Overridden from List */ 255 public void replaceAll(UnaryOperator<Object> operator) { 256 throw uoroex(); 257 } 258 259 @Override /* Overridden from List */ 260 public boolean retainAll(Collection<?> c) { 261 throw uoroex(); 262 } 263 264 @Override /* Overridden from List */ 265 public Object set(int location, Object object) { 266 throw uoroex(); 267 } 268 269 @Override /* Overridden from List */ 270 public void sort(Comparator<? super Object> c) { 271 throw uoroex(); 272 } 273 274 @Override /* Overridden from List */ 275 public List<Object> subList(int start, int end) { 276 return u(new ArrayList<>(this).subList(start, end)); 277 } 278 279 private static Iterator<Object> readOnlyIterator(Iterator<Object> it) { 280 return new Iterator<>() { 281 @Override public boolean hasNext() { return it.hasNext(); } 282 @Override public Object next() { return it.next(); } 283 @Override public void remove() { throw uoroex(); } 284 @Override public void forEachRemaining(Consumer<? super Object> action) { it.forEachRemaining(action); } 285 }; 286 } 287 288 private static ListIterator<Object> readOnlyListIterator(ListIterator<Object> it) { 289 return new ListIterator<>() { 290 @Override public boolean hasNext() { return it.hasNext(); } 291 @Override public Object next() { return it.next(); } 292 @Override public boolean hasPrevious() { return it.hasPrevious(); } 293 @Override public Object previous() { return it.previous(); } 294 @Override public int nextIndex() { return it.nextIndex(); } 295 @Override public int previousIndex() { return it.previousIndex(); } 296 @Override public void remove() { throw uoroex(); } 297 @Override public void set(Object e) { throw uoroex(); } 298 @Override public void add(Object e) { throw uoroex(); } 299 @Override public void forEachRemaining(Consumer<? super Object> action) { it.forEachRemaining(action); } 300 }; 301 } 302 } 303 304 private static final long serialVersionUID = 1L; 305 /** 306 * An empty read-only JsonList. 307 * 308 * @serial exclude 309 */ 310 @SuppressWarnings({ 311 "java:S2386" // Public static final field accessed externally, cannot be protected 312 }) 313 public static final JsonList EMPTY_LIST = new JsonList() { 314 private static final long serialVersionUID = 1L; 315 316 @Override /* Overridden from List */ 317 public void add(int location, Object object) { 318 throw uoroex(); 319 } 320 321 @Override /* Overridden from List */ 322 public ListIterator<Object> listIterator(int location) { 323 return Collections.emptyList().listIterator(location); 324 } 325 326 @Override /* Overridden from List */ 327 public Object remove(int location) { 328 throw uoroex(); 329 } 330 331 @Override /* Overridden from List */ 332 public Object set(int location, Object object) { 333 throw uoroex(); 334 } 335 336 @Override /* Overridden from List */ 337 public List<Object> subList(int start, int end) { 338 return Collections.emptyList().subList(start, end); 339 } 340 }; 341 342 /** 343 * Construct an empty list. 344 * 345 * @return An empty list. 346 */ 347 public static JsonList create() { 348 return new JsonList(); 349 } 350 351 /** 352 * Construct a list initialized with the specified list. 353 * 354 * @param values 355 * The list to copy. 356 * <br>Can be <jk>null</jk> (returns <jk>null</jk>). 357 * @return A new list or <jk>null</jk> if the list was <jk>null</jk>. 358 */ 359 public static JsonList of(Collection<?> values) { 360 return values == null ? null : new JsonList(values); 361 } 362 363 /** 364 * Construct a list initialized with the specified values. 365 * 366 * @param values The values to add to this list. 367 * @return A new list, never <jk>null</jk>. 368 */ 369 public static JsonList of(Object...values) { 370 return new JsonList(values); 371 } 372 373 /** 374 * Convenience method for creating a list of array objects. 375 * 376 * @param values The initial values. 377 * @return A new list. 378 */ 379 public static JsonList ofArrays(Object[]...values) { 380 var l = new JsonList(); 381 Collections.addAll(l, values); 382 return l; 383 } 384 385 /** 386 * Convenience method for creating a list of collection objects. 387 * 388 * @param values The initial values. 389 * @return A new list. 390 */ 391 public static JsonList ofCollections(Collection<?>...values) { 392 var l = new JsonList(); 393 Collections.addAll(l, values); 394 return l; 395 } 396 397 /** 398 * Construct a list initialized with the specified JSON string. 399 * 400 * @param json 401 * The JSON text to parse. 402 * <br>Can be normal or simplified JSON. 403 * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>. 404 * @throws ParseException Malformed input encountered. 405 */ 406 public static JsonList ofString(CharSequence json) throws ParseException { 407 return json == null ? new JsonList() : new JsonList(json); 408 } 409 410 /** 411 * Construct a list initialized with the specified reader containing JSON. 412 * 413 * @param json 414 * The reader containing JSON text to parse. 415 * <br>Can contain normal or simplified JSON. 416 * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>. 417 * @throws ParseException Malformed input encountered. 418 */ 419 public static JsonList ofString(Reader json) throws ParseException { 420 return json == null ? new JsonList() : new JsonList(json); 421 } 422 423 /** 424 * Parses a string that can consist of either a JSON array or comma-delimited list. 425 * 426 * <p> 427 * The type of string is auto-detected. 428 * 429 * @param s The string to parse. 430 * @return A new list (empty if the input was <jk>null</jk> or empty), never <jk>null</jk>. 431 * @throws ParseException Malformed input encountered. 432 */ 433 public static JsonList ofJsonOrCdl(String s) throws ParseException { 434 if (Shorts.ie(s)) // NOAI 435 return new JsonList(); 436 if (! isProbablyJsonArray(s, true)) 437 return new JsonList((Object[])splita(s.trim(), ',')); 438 return new JsonList(s); 439 } 440 441 /** 442 * Construct a list initialized with the specified string. 443 * 444 * @param in The input being parsed. 445 * @param p 446 * The parser to use to parse the input. 447 * <br>If <jk>null</jk>, uses {@link JsonParser}. 448 * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>. 449 * @throws ParseException Malformed input encountered. 450 */ 451 public static JsonList ofString(CharSequence in, Parser p) throws ParseException { 452 return in == null ? new JsonList() : new JsonList(in, p); 453 } 454 455 /** 456 * Construct a list initialized with the specified string. 457 * 458 * @param in 459 * The reader containing the input being parsed. 460 * <br>Can contain normal or simplified JSON. 461 * @param p 462 * The parser to use to parse the input. 463 * <br>If <jk>null</jk>, uses {@link JsonParser}. 464 * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>. 465 * @throws ParseException Malformed input encountered. 466 */ 467 @SuppressWarnings({ 468 "java:S1172" // Parameter reserved for future parser context support 469 }) 470 public static JsonList ofString(Reader in, Parser p) throws ParseException { 471 return in == null ? new JsonList() : new JsonList(in); 472 } 473 474 /** 475 * Construct an empty list. 476 */ 477 public JsonList() {} 478 479 /** 480 * Construct an empty list with the specified bean context. 481 * 482 * @param session The bean session to use for creating beans. 483 */ 484 public JsonList(MarshallingSession session) { 485 super(session); 486 } 487 488 /** 489 * Construct a list initialized with the specified JSON. 490 * 491 * @param json 492 * The JSON text to parse. 493 * <br>Must be strict (RFC 8259) JSON; for JSON5 input use {@link Json5List} or pass 494 * {@link Json5Parser#DEFAULT} explicitly. 495 * @throws ParseException Malformed input encountered. 496 */ 497 public JsonList(CharSequence json) throws ParseException { 498 this(json, JsonParser.DEFAULT); 499 } 500 501 /** 502 * Construct a list initialized with the specified string. 503 * 504 * @param in 505 * The input being parsed. 506 * <br>Can be <jk>null</jk> or empty (results in an empty list; no parsing occurs). 507 * @param p 508 * The parser to use to parse the input. 509 * <br>If <jk>null</jk>, uses {@link JsonParser}. 510 * @throws ParseException Malformed input encountered. 511 */ 512 public JsonList(CharSequence in, Parser p) throws ParseException { 513 super(in, p == null ? JsonParser.DEFAULT : p); 514 } 515 516 /** 517 * Construct a list initialized with the specified list. 518 * 519 * @param copyFrom 520 * The list to copy. 521 * <br>Can be <jk>null</jk> (results in an empty list). 522 */ 523 public JsonList(Collection<?> copyFrom) { 524 super(copyFrom); 525 } 526 527 /** 528 * Construct a list initialized with the contents. 529 * 530 * @param entries The entries to add to this list. 531 */ 532 public JsonList(Object...entries) { 533 super(entries); 534 } 535 536 /** 537 * Construct a list initialized with the specified reader containing JSON. 538 * 539 * @param json 540 * The reader containing JSON text to parse. 541 * <br>Must be strict (RFC 8259) JSON; for JSON5 input use {@link Json5List} or pass 542 * {@link Json5Parser#DEFAULT} explicitly. 543 * @throws ParseException Malformed input encountered. 544 */ 545 public JsonList(Reader json) throws ParseException { 546 this(json, JsonParser.DEFAULT); 547 } 548 549 /** 550 * Construct a list initialized with the specified string. 551 * 552 * @param in 553 * The reader containing the input being parsed. 554 * <br>Must be strict (RFC 8259) JSON. 555 * @param p 556 * The parser to use to parse the input. 557 * <br>If <jk>null</jk>, uses {@link JsonParser}. 558 * @throws ParseException Malformed input encountered. 559 */ 560 public JsonList(Reader in, Parser p) throws ParseException { 561 super(in, p == null ? JsonParser.DEFAULT : p); 562 } 563 564 @Override /* Overridden from MarshalledList */ 565 public JsonList append(Collection<?> values) { 566 super.append(values); 567 return this; 568 } 569 570 @Override /* Overridden from MarshalledList */ 571 public JsonList append(Object value) { 572 super.append(value); 573 return this; 574 } 575 576 @Override /* Overridden from MarshalledList */ 577 public JsonList append(Object...values) { 578 super.append(values); 579 return this; 580 } 581 582 @Override /* Overridden from MarshalledList */ 583 public JsonList appendIf(boolean flag, Object value) { 584 super.appendIf(flag, value); 585 return this; 586 } 587 588 @Override /* Overridden from MarshalledList */ 589 public <T> JsonList appendIf(Predicate<T> test, T value) { 590 super.appendIf(test, value); 591 return this; 592 } 593 594 @Override /* Overridden from MarshalledList */ 595 public JsonList appendReverse(List<?> values) { 596 super.appendReverse(values); 597 return this; 598 } 599 600 @Override /* Overridden from MarshalledList */ 601 public JsonList appendReverse(Object...values) { 602 super.appendReverse(values); 603 return this; 604 } 605 606 /** 607 * Serializes this list to a standard JSON string. 608 * 609 * @return This object as a standard JSON string. 610 */ 611 public String toJson() { 612 return Json.of(this); 613 } 614 615 /** 616 * Serializes this list to a JSON5 string. 617 * 618 * <p> 619 * A synonym for {@link #toString()}. 620 * 621 * @return This object as a JSON5 string. 622 */ 623 public String toJson5() { 624 return Json5.of(this); 625 } 626 627 /** 628 * Serializes this list to a JSON Lines string. 629 * 630 * @return This object as a JSON Lines string. 631 */ 632 public String toJsonl() { 633 return Jsonl.of(this); 634 } 635 636 /** 637 * Serializes this list to a canonical JSON string (RFC 8785). 638 * 639 * @return This object as a canonical JSON string. 640 */ 641 public String toJcs() { 642 return Jcs.of(this); 643 } 644 645 /** 646 * Serializes this list to an HJSON string. 647 * 648 * @return This object as an HJSON string. 649 */ 650 public String toHjson() { 651 return Hjson.of(this); 652 } 653 654 /** 655 * Converts this object into the specified class type. 656 * 657 * <p> 658 * This method performs a round-trip serialization and deserialization to convert the list into the target type. 659 * 660 * <h5 class='section'>Notes:</h5><ul> 661 * <li class='warn'>The current implementation uses a serialization round-trip which may be inefficient for 662 * frequent conversions of large objects. Consider caching results or using direct object conversion where possible. 663 * </ul> 664 * 665 * @param cm The class type to convert this object to. 666 * @return A converted object. 667 */ 668 public Object cast(ClassMeta<?> cm) { 669 try { 670 return Json5.to(Json5.of(this), cm); 671 } catch (ParseException | SerializeException e) { 672 throw toRex(e); 673 } 674 } 675 676 @Override /* Overridden from MarshalledList */ 677 public JsonList getList(int index) { 678 return get(index, JsonList.class); 679 } 680 681 @Override /* Overridden from MarshalledList */ 682 public JsonMap getMap(int index) { 683 return get(index, JsonMap.class); 684 } 685 686 @Override /* Overridden from MarshalledList */ 687 public JsonList modifiable() { 688 if (isUnmodifiable()) 689 return new JsonList(this); 690 return this; 691 } 692 693 @Override /* Overridden from MarshalledList */ 694 public JsonList session(MarshallingSession session) { 695 super.session(session); 696 return this; 697 } 698 699 @Override /* Overridden from MarshalledList */ 700 public JsonList setBeanSession(MarshallingSession value) { 701 super.setBeanSession(value); 702 return this; 703 } 704 705 @Override /* Overridden from Object */ 706 public String toString() { 707 return Json.of(this); 708 } 709 710 @Override /* Overridden from MarshalledList */ 711 public JsonList unmodifiable() { 712 if (this instanceof Unmodifiable this2) 713 return this2; 714 return new Unmodifiable(this); 715 } 716 717 /** 718 * Convenience method for serializing this JsonList to the specified Writer using the {@link Json#DEFAULT} 719 * marshaller. 720 * 721 * @param w The writer to send the serialized contents of this object. 722 * @return This object. 723 * @throws IOException If a problem occurred trying to write to the writer. 724 * @throws SerializeException If a problem occurred trying to convert the output. 725 */ 726 public JsonList writeTo(Writer w) throws IOException, SerializeException { 727 Json.DEFAULT.write(this, w); 728 return this; 729 } 730}