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 java.util.Collections.*; 020import static org.apache.juneau.commons.utils.Shorts.*; 021 022import java.io.*; 023import java.util.*; 024import java.util.function.*; 025 026import org.apache.juneau.marshall.*; 027import org.apache.juneau.marshall.json.*; 028import org.apache.juneau.marshall.json5.*; 029import org.apache.juneau.marshall.marshaller.*; 030import org.apache.juneau.marshall.marshaller.Json; 031import org.apache.juneau.marshall.parser.*; 032import org.apache.juneau.marshall.serializer.*; 033 034/** 035 * Java implementation of a JSON object. 036 * 037 * <p> 038 * This class is a subclass of {@link MarshalledMap} that carries strict-JSON-specific surface, including 039 * strict-JSON {@link #toString()} (via {@link Json#of(Object)}), {@link JsonParser}-defaulting 040 * {@link CharSequence} / {@link Reader} constructors, and convenience methods for serializing to the 041 * various JSON dialects (JSON5, JSON Lines, canonical JSON, HJSON). 042 * 043 * <p> 044 * <b>v10.0 behavioral break:</b> {@code toString()} now produces strict (RFC 8259) JSON (was JSON5), 045 * and the {@link CharSequence} / {@link Reader} constructors now default to {@link JsonParser#DEFAULT} 046 * (was {@link Json5Parser#DEFAULT}). For the previous JSON5 behavior use {@link Json5Map}. 047 * 048 * <p> 049 * Note that the use of this class is optional for generating JSON. The serializers will accept any objects that implement the 050 * {@link Map} interface. But this class provides some useful additional functionality when working with 051 * JSON models constructed from Java Collections Framework objects. For example, a constructor is provided for 052 * converting a JSON object string directly into a {@link Map}. It also contains accessor methods for to avoid common 053 * typecasting when accessing elements in a list. 054 * 055 * <h5 class='section'>Example:</h5> 056 * <p class='bjava'> 057 * <jc>// Construct an empty Map</jc> 058 * JsonMap <jv>map</jv> = JsonMap.<jsm>of</jsm>(); 059 * 060 * <jc>// Construct a Map from JSON</jc> 061 * <jv>map</jv> = JsonMap.<jsm>ofString</jsm>(<js>"{a:'A',b:{c:'C',d:123}}"</js>); 062 * 063 * <jc>// Construct a Map using the append method</jc> 064 * <jv>map</jv> = JsonMap.<jsm>of</jsm>().a(<js>"foo"</js>,<js>"x"</js>).a(<js>"bar"</js>,123).a(<js>"baz"</js>,<jk>true</jk>); 065 * 066 * <jc>// Construct a Map from XML generated by XmlSerializer</jc> 067 * String <jv>xml</jv> = <js>"<object><a type='string'>A</a><b type='object'><c type='string'>C</c><d type='number'>123</d></b></object>"</js>; 068 * <jv>map</jv> = JsonMap.<jsm>of</jsm>(<jv>xml</jv>, XmlParser.<jsf>DEFAULT</jsf>); 069 * 070 * <jc>// Construct a Map from a URL GET parameter string generated by UrlEncodingParser</jc> 071 * String <jv>urlParams</jv> = <js>"?a='A'&b={c:'C',d:123}"</js>; 072 * <jv>map</jv> = JsonMap.<jsm>of</jsm>(<jv>urlParams</jv>, UrlEncodingParser.<jsf>DEFAULT</jsf>); 073 * 074 * <jc>// Construct JSON from JsonMap</jc> 075 * <jv>map</jv> = JsonMap.<jsm>ofString</jsm>(<js>"{foo:'bar'},{baz:[123,true]}"</js>); 076 * String <jv>json</jv> = <jv>map</jv>.toString(); <jc>// Produces "{foo:'bar'},{baz:[123,true]}"</jc> 077 * <jv>json</jv> = <jv>map</jv>.toString(JsonSerializer.<jsf>DEFAULT</jsf>); <jc>// Equivalent</jc> 078 * <jv>json</jv> = JsonSerializer.<jsf>DEFAULT</jsf>.write(<jv>map</jv>); <jc>// Equivalent</jc> 079 * 080 * <jc>// Get a map entry as an Integer</jc> 081 * <jv>map</jv> = JsonMap.<jsm>ofString</jsm>(<js>"{foo:123}"</js>); 082 * Integer <jv>integer</jv> = <jv>map</jv>.getInt(<js>"foo"</js>); 083 * <jv>integer</jv> = <jv>map</jv>.get(Integer.<jk>class</jk>, <js>"foo"</js>); <jc>// Equivalent</jc> 084 * 085 * <jc>// Add an inner map</jc> 086 * JsonMap <jv>map1</jv> = JsonMap.<jsm>ofString</jsm>(<js>"{a:1}"</js>); 087 * JsonMap <jv>map2</jv> = JsonMap.<jsm>ofString</jsm>(<js>"{b:2}"</js>).setInner(<jv>map1</jv>); 088 * <jk>int</jk> <jv>_int</jv> = <jv>map2</jv>.getInt(<js>"a"</js>); <jc>// a == 1 </jc> 089 * </p> 090 * 091 * <h5 class='section'>Notes:</h5><ul> 092 * <li class='warn'>This class is not thread safe. 093 * </ul> 094 */ 095@SuppressWarnings({ 096 "java:S110", // JsonMap -> MarshalledMap -> LinkedHashMap -> HashMap -> AbstractMap: depth comes from the JDK map hierarchy 097 "java:S2160", // equals() / hashCode() inherited from MarshalledMap; map equality is element-based 098 "java:S9149" // Per-type static factories intentionally shadow the parent's. 099}) 100public class JsonMap extends MarshalledMap { 101 102 private static class Unmodifiable extends JsonMap { 103 private static final long serialVersionUID = 1L; 104 105 @SuppressWarnings({ 106 "synthetic-access" // Access to outer class members is intentional 107 }) 108 Unmodifiable(JsonMap contents) { 109 if (nn(contents)) 110 contents.forEach(super::put); 111 } 112 113 @Override 114 public void clear() { 115 throw uoroex(); 116 } 117 118 @Override 119 public Object compute(String key, BiFunction<? super String,? super Object,?> remappingFunction) { 120 throw uoroex(); 121 } 122 123 @Override 124 public Object computeIfAbsent(String key, Function<? super String,?> mappingFunction) { 125 throw uoroex(); 126 } 127 128 @Override 129 public Object computeIfPresent(String key, BiFunction<? super String,? super Object,?> remappingFunction) { 130 throw uoroex(); 131 } 132 133 @Override 134 public Set<Map.Entry<String,Object>> entrySet() { 135 return u(super.entrySet()); 136 } 137 138 @Override 139 public boolean isUnmodifiable() { return true; } 140 141 @Override 142 public Set<String> keySet() { 143 return u(super.keySet()); 144 } 145 146 @Override 147 public Object merge(String key, Object value, BiFunction<? super Object,? super Object,?> remappingFunction) { 148 throw uoroex(); 149 } 150 151 @Override 152 public Object put(String key, Object val) { 153 throw uoroex(); 154 } 155 156 @Override 157 public void putAll(Map<? extends String,?> m) { 158 throw uoroex(); 159 } 160 161 @Override 162 public Object putIfAbsent(String key, Object value) { 163 throw uoroex(); 164 } 165 166 @Override 167 public Object remove(Object key) { 168 throw uoroex(); 169 } 170 171 @Override 172 public boolean remove(Object key, Object value) { 173 throw uoroex(); 174 } 175 176 @Override 177 public Object replace(String key, Object value) { 178 throw uoroex(); 179 } 180 181 @Override 182 public boolean replace(String key, Object oldValue, Object newValue) { 183 throw uoroex(); 184 } 185 186 @Override 187 public void replaceAll(BiFunction<? super String,? super Object,?> function) { 188 throw uoroex(); 189 } 190 191 @Override 192 public Collection<Object> values() { 193 return Collections.unmodifiableCollection(super.values()); 194 } 195 } 196 197 private static final long serialVersionUID = 1L; 198 199 /** 200 * An empty read-only JsonMap. 201 * 202 * @serial exclude 203 */ 204 @SuppressWarnings({ 205 "java:S2386" // Public static final field accessed externally, cannot be protected 206 }) 207 public static final JsonMap EMPTY_MAP = new JsonMap() { 208 209 private static final long serialVersionUID = 1L; 210 211 @Override /* Overridden from Map */ 212 public Set<Map.Entry<String,Object>> entrySet() { 213 return Collections.<String,Object>emptyMap().entrySet(); 214 } 215 216 @Override /* Overridden from Map */ 217 public Set<String> keySet() { 218 return Collections.<String,Object>emptyMap().keySet(); 219 } 220 221 @Override /* Overridden from Map */ 222 public Object put(String key, Object value) { 223 throw uoroex(); 224 } 225 226 @Override /* Overridden from Map */ 227 public Object remove(Object key) { 228 throw uoroex(); 229 } 230 231 @Override /* Overridden from Map */ 232 public Collection<Object> values() { 233 return emptyMap().values(); 234 } 235 }; 236 237 /** 238 * Construct an empty map. 239 * 240 * @return An empty map. 241 */ 242 public static JsonMap create() { 243 return new JsonMap(); 244 } 245 246 /** 247 * Construct an empty filtered map. 248 * 249 * @return An empty map. 250 */ 251 public static JsonMap filteredMap() { 252 return create().filtered(); 253 } 254 255 /** 256 * Construct a filtered map initialized with the specified key/value pairs. 257 * 258 * <p> 259 * Same as {@link #of(Object...)} but calls {@link #filtered()} on the created map. 260 * 261 * @param keyValuePairs A list of key/value pairs to add to this map. 262 * @return A new map, never <jk>null</jk>. 263 */ 264 public static JsonMap filteredMap(Object...keyValuePairs) { 265 return new JsonMap(keyValuePairs).filtered(); 266 } 267 268 /** 269 * Construct a map initialized with the specified map. 270 * 271 * @param values 272 * The map to copy. 273 * <br>Can be <jk>null</jk> (returns <jk>null</jk>). 274 * <br>Keys will be converted to strings using {@link Object#toString()}. 275 * @return A new map or <jk>null</jk> if the map was <jk>null</jk>. 276 */ 277 public static JsonMap of(Map<?,?> values) { 278 return values == null ? null : new JsonMap(values); 279 } 280 281 /** 282 * Construct a map initialized with the specified key/value pairs. 283 * 284 * <h5 class='section'>Examples:</h5> 285 * <p class='bjava'> 286 * JsonMap <jv>map</jv> = <jk>new</jk> JsonMap(<js>"key1"</js>,<js>"val1"</js>,<js>"key2"</js>,<js>"val2"</js>); 287 * </p> 288 * 289 * @param keyValuePairs A list of key/value pairs to add to this map. 290 * @return A new map, never <jk>null</jk>. 291 */ 292 public static JsonMap of(Object...keyValuePairs) { 293 return new JsonMap(keyValuePairs); 294 } 295 296 /** 297 * Construct a map initialized with the specified JSON string. 298 * 299 * @param json 300 * The JSON text to parse. 301 * <br>Can be normal or simplified JSON. 302 * @return A new map (empty if the input was <jk>null</jk>), never <jk>null</jk>. 303 * @throws ParseException Malformed input encountered. 304 */ 305 public static JsonMap ofString(CharSequence json) throws ParseException { 306 return json == null ? new JsonMap() : new JsonMap(json); 307 } 308 309 /** 310 * Construct a map initialized with the specified reader containing JSON. 311 * 312 * @param json 313 * The reader containing JSON text to parse. 314 * <br>Can contain normal or simplified JSON. 315 * @return A new map (empty if the input was <jk>null</jk>), never <jk>null</jk>. 316 * @throws ParseException Malformed input encountered. 317 */ 318 public static JsonMap ofString(Reader json) throws ParseException { 319 return json == null ? new JsonMap() : new JsonMap(json); 320 } 321 322 /** 323 * Construct a map initialized with the specified string. 324 * 325 * @param in The input being parsed. 326 * @param p 327 * The parser to use to parse the input. 328 * <br>If <jk>null</jk>, uses {@link JsonParser}. 329 * @return A new map (empty if the input was <jk>null</jk>), never <jk>null</jk>. 330 * @throws ParseException Malformed input encountered. 331 */ 332 public static JsonMap ofString(CharSequence in, Parser p) throws ParseException { 333 return in == null ? new JsonMap() : new JsonMap(in, p); 334 } 335 336 /** 337 * Construct a map initialized with the specified string. 338 * 339 * @param in 340 * The reader containing the input being parsed. 341 * <br>Can contain normal or simplified JSON. 342 * @param p 343 * The parser to use to parse the input. 344 * <br>If <jk>null</jk>, uses {@link JsonParser}. 345 * @return A new map (empty if the input was <jk>null</jk>), never <jk>null</jk>. 346 * @throws ParseException Malformed input encountered. 347 */ 348 public static JsonMap ofString(Reader in, Parser p) throws ParseException { 349 return in == null ? new JsonMap() : new JsonMap(in, p); 350 } 351 352 /** 353 * Construct an empty map. 354 */ 355 public JsonMap() {} 356 357 /** 358 * Construct an empty map with the specified bean context. 359 * 360 * @param session The bean session to use for creating beans. 361 */ 362 public JsonMap(MarshallingSession session) { 363 super(session); 364 } 365 366 /** 367 * Construct a map initialized with the specified JSON. 368 * 369 * @param json 370 * The JSON text to parse. 371 * <br>Must be strict (RFC 8259) JSON; for JSON5 input use {@link Json5Map} or pass 372 * {@link Json5Parser#DEFAULT} explicitly. 373 * @throws ParseException Malformed input encountered. 374 */ 375 public JsonMap(CharSequence json) throws ParseException { 376 this(json, JsonParser.DEFAULT); 377 } 378 379 /** 380 * Construct a map initialized with the specified string. 381 * 382 * @param in 383 * The input being parsed. 384 * <br>Can be <jk>null</jk> or empty (results in an empty map; no parsing occurs). 385 * @param p 386 * The parser to use to parse the input. 387 * <br>If <jk>null</jk>, uses {@link JsonParser}. 388 * @throws ParseException Malformed input encountered. 389 */ 390 public JsonMap(CharSequence in, Parser p) throws ParseException { 391 super(in, p == null ? JsonParser.DEFAULT : p); 392 } 393 394 /** 395 * Construct a map initialized with the specified map. 396 * 397 * @param in 398 * The map to copy. 399 * <br>Can be <jk>null</jk> (results in an empty map). 400 * <br>Keys will be converted to strings using {@link Object#toString()}. 401 */ 402 public JsonMap(Map<?,?> in) { 403 super(in); 404 } 405 406 /** 407 * Construct a map initialized with the specified key/value pairs. 408 * 409 * <h5 class='section'>Examples:</h5> 410 * <p class='bjava'> 411 * JsonMap <jv>map</jv> = <jk>new</jk> JsonMap(<js>"key1"</js>,<js>"val1"</js>,<js>"key2"</js>,<js>"val2"</js>); 412 * </p> 413 * 414 * @param keyValuePairs A list of key/value pairs to add to this map. 415 */ 416 public JsonMap(Object...keyValuePairs) { 417 super(keyValuePairs); 418 } 419 420 /** 421 * Construct a map initialized with the specified reader containing JSON. 422 * 423 * @param json 424 * The reader containing JSON text to parse. 425 * <br>Must be strict (RFC 8259) JSON; for JSON5 input use {@link Json5Map} or pass 426 * {@link Json5Parser#DEFAULT} explicitly. 427 * @throws ParseException Malformed input encountered. 428 */ 429 public JsonMap(Reader json) throws ParseException { 430 this(json, JsonParser.DEFAULT); 431 } 432 433 /** 434 * Construct a map initialized with the specified string. 435 * 436 * @param in 437 * The reader containing the input being parsed. 438 * <br>Must be strict (RFC 8259) JSON. 439 * @param p 440 * The parser to use to parse the input. 441 * <br>If <jk>null</jk>, uses {@link JsonParser}. 442 * @throws ParseException Malformed input encountered. 443 */ 444 public JsonMap(Reader in, Parser p) throws ParseException { 445 super(in, p == null ? JsonParser.DEFAULT : p); 446 } 447 448 @Override /* Overridden from MarshalledMap */ 449 public JsonMap append(Map<String,Object> values) { 450 super.append(values); 451 return this; 452 } 453 454 @Override /* Overridden from MarshalledMap */ 455 public JsonMap append(String key, Object value) { 456 super.append(key, value); 457 return this; 458 } 459 460 @Override /* Overridden from MarshalledMap */ 461 @SuppressWarnings({ 462 "unchecked", // Generic varargs is safe here: values are only iterated, not stored into a typed array. 463 "varargs" // values is only forwarded to super.appendFirst(), never stored or exposed, so heap pollution cannot occur 464 }) 465 public <T> JsonMap appendFirst(Predicate<T> test, String key, T...values) { 466 super.appendFirst(test, key, values); 467 return this; 468 } 469 470 @Override /* Overridden from MarshalledMap */ 471 public JsonMap appendIf(boolean flag, String key, Object value) { 472 super.appendIf(flag, key, value); 473 return this; 474 } 475 476 @Override /* Overridden from MarshalledMap */ 477 public <T> JsonMap appendIf(Predicate<T> test, String key, T value) { 478 super.appendIf(test, key, value); 479 return this; 480 } 481 482 @Override /* Overridden from MarshalledMap */ 483 public JsonMap appendIfAbsent(String key, Object value) { 484 super.appendIfAbsent(key, value); 485 return this; 486 } 487 488 @Override /* Overridden from MarshalledMap */ 489 public <T> JsonMap appendIfAbsentIf(Predicate<T> predicate, String key, T value) { 490 super.appendIfAbsentIf(predicate, key, value); 491 return this; 492 } 493 494 /** 495 * Serializes this map to a standard JSON string. 496 * 497 * @return This object as a standard JSON string. 498 */ 499 public String toJson() { 500 return Json.of(this); 501 } 502 503 /** 504 * Serializes this map to a JSON5 string. 505 * 506 * <p> 507 * A synonym for {@link #toString()}. 508 * 509 * @return This object as a JSON5 string. 510 */ 511 public String toJson5() { 512 return Json5.of(this); 513 } 514 515 /** 516 * Serializes this map to a JSON Lines string. 517 * 518 * @return This object as a JSON Lines string. 519 */ 520 public String toJsonl() { 521 return Jsonl.of(this); 522 } 523 524 /** 525 * Serializes this map to a canonical JSON string (RFC 8785). 526 * 527 * @return This object as a canonical JSON string. 528 */ 529 public String toJcs() { 530 return Jcs.of(this); 531 } 532 533 /** 534 * Serializes this map to an HJSON string. 535 * 536 * @return This object as an HJSON string. 537 */ 538 public String toHjson() { 539 return Hjson.of(this); 540 } 541 542 /** 543 * Serializes this map to a readable (indented) JSON5 string using {@link Json5Serializer#DEFAULT_READABLE}. 544 * 545 * @return This object serialized as a readable JSON5 string. 546 */ 547 public String toReadableJson5() { 548 if (Json5Serializer.DEFAULT_READABLE == null) 549 return s(this); 550 return Json5Serializer.DEFAULT_READABLE.toString(this); 551 } 552 553 @Override /* Overridden from MarshalledMap */ 554 public JsonMap exclude(String...keys) { 555 var m2 = new JsonMap(); 556 this.forEach((k, v) -> { 557 var exclude = false; 558 for (var kk : keys) 559 if (kk.equals(k)) 560 exclude = true; 561 if (! exclude) 562 m2.put(k, v); 563 }); 564 return m2; 565 } 566 567 @Override /* Overridden from MarshalledMap */ 568 public JsonMap filtered() { 569 super.filtered(); 570 return this; 571 } 572 573 @Override /* Overridden from MarshalledMap */ 574 public JsonMap filtered(Predicate<Object> value) { 575 super.filtered(value); 576 return this; 577 } 578 579 @Override /* Overridden from MarshalledMap */ 580 public JsonList findList(String...keys) { 581 return find(JsonList.class, keys); 582 } 583 584 @Override /* Overridden from MarshalledMap */ 585 public JsonMap findMap(String...keys) { 586 return find(JsonMap.class, keys); 587 } 588 589 @Override /* Overridden from MarshalledMap */ 590 public JsonList getList(String key) { 591 return get(key, JsonList.class); 592 } 593 594 @Override /* Overridden from MarshalledMap */ 595 public JsonList getList(String key, boolean createIfNotExists) { 596 JsonList m = getWithDefault(key, null, JsonList.class); 597 if (m == null && createIfNotExists) { 598 m = new JsonList(); 599 put(key, m); 600 } 601 return m; 602 } 603 604 /** 605 * Returns the specified entry value converted to a {@link JsonList}. 606 * 607 * @param key The key. 608 * @param defVal The default value if the map doesn't contain the specified mapping. 609 * @return The converted value, or the default value if the map contains no mapping for this key. 610 * @throws InvalidDataConversionException If value cannot be converted. 611 */ 612 public JsonList getList(String key, JsonList defVal) { 613 return getWithDefault(key, defVal, JsonList.class); 614 } 615 616 @Override /* Overridden from MarshalledMap */ 617 public JsonMap getMap(String key) { 618 return get(key, JsonMap.class); 619 } 620 621 @Override /* Overridden from MarshalledMap */ 622 public JsonMap getMap(String key, boolean createIfNotExists) { 623 var m = getWithDefault(key, null, JsonMap.class); 624 if (m == null && createIfNotExists) { 625 m = new JsonMap(); 626 put(key, m); 627 } 628 return m; 629 } 630 631 /** 632 * Returns the specified entry value converted to a {@link JsonMap}. 633 * 634 * @param key The key. 635 * @param defVal The default value if the map doesn't contain the specified mapping. 636 * @return The converted value, or the default value if the map contains no mapping for this key. 637 * @throws InvalidDataConversionException If value cannot be converted. 638 */ 639 public JsonMap getMap(String key, JsonMap defVal) { 640 return getWithDefault(key, defVal, JsonMap.class); 641 } 642 643 @Override /* Overridden from MarshalledMap */ 644 public JsonMap include(String...keys) { 645 var m2 = new JsonMap(); 646 this.forEach((k, v) -> { 647 for (var kk : keys) 648 if (kk.equals(k)) 649 m2.put(kk, v); 650 }); 651 return m2; 652 } 653 654 @Override /* Overridden from MarshalledMap */ 655 public JsonMap inner(Map<String,Object> inner) { 656 super.inner(inner); 657 return this; 658 } 659 660 @Override /* Overridden from MarshalledMap */ 661 public JsonMap keepAll(String...keys) { 662 super.keepAll(keys); 663 return this; 664 } 665 666 @Override /* Overridden from MarshalledMap */ 667 public JsonMap modifiable() { 668 if (isUnmodifiable()) 669 return new JsonMap(this); 670 return this; 671 } 672 673 @Override /* Overridden from MarshalledMap */ 674 protected JsonMap newMarshalledMap(MarshallingSession bs) { 675 return new JsonMap(bs); 676 } 677 678 /** 679 * Convenience method for inserting JSON directly into an attribute on this object. 680 * 681 * <p> 682 * The JSON text can be an object (i.e. <js>"{...}"</js>) or an array (i.e. <js>"[...]"</js>). 683 * 684 * <p> 685 * Input must be strict (RFC 8259) JSON; for JSON5 use {@link Json5Map#putJson5(String,String)}. 686 * 687 * @param key The key. 688 * @param json The JSON text that will be parsed into an Object and then inserted into this map. 689 * @throws ParseException Malformed input encountered. 690 */ 691 public void putJson(String key, String json) throws ParseException { 692 this.put(key, Json.to(json, Object.class)); 693 } 694 695 @Override /* Overridden from MarshalledMap */ 696 public JsonMap session(MarshallingSession session) { 697 super.session(session); 698 return this; 699 } 700 701 @Override /* Overridden from MarshalledMap */ 702 public JsonMap setBeanSession(MarshallingSession value) { 703 super.setBeanSession(value); 704 return this; 705 } 706 707 @Override /* Overridden from Object */ 708 public String toString() { 709 return Json.of(this); 710 } 711 712 @Override /* Overridden from MarshalledMap */ 713 public JsonMap unmodifiable() { 714 if (this instanceof Unmodifiable this2) 715 return this2; 716 return new Unmodifiable(this); 717 } 718 719 /** 720 * Convenience method for serializing this map to the specified <c>Writer</c> using the 721 * {@link Json#DEFAULT} marshaller. 722 * 723 * @param w The writer to serialize this object to. 724 * @return This object. 725 * @throws IOException If a problem occurred trying to write to the writer. 726 * @throws SerializeException If a problem occurred trying to convert the output. 727 */ 728 public JsonMap writeTo(Writer w) throws IOException, SerializeException { 729 Json.DEFAULT.write(this, w); 730 return this; 731 } 732}