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.serializer; 018 019import static java.util.Collections.*; 020import static org.apache.juneau.commons.utils.CollectionUtils.*; 021import static org.apache.juneau.commons.utils.Shorts.*; 022import static org.apache.juneau.commons.utils.StringUtils.*; 023import static org.apache.juneau.commons.utils.SystemUtils.*; 024 025import java.io.*; 026import java.net.*; 027import java.util.*; 028import java.util.function.*; 029 030import org.apache.juneau.commons.collections.*; 031import org.apache.juneau.commons.http.*; 032import org.apache.juneau.marshall.*; 033import org.apache.juneau.marshall.json5.*; 034import org.apache.juneau.marshall.soap.*; 035import org.apache.juneau.marshall.stream.*; 036 037/** 038 * Parent class for all Juneau serializers. 039 * 040 * <h5 class='topic'>Description</h5> 041 * <p> 042 * Base serializer class that serves as the parent class for all serializers. 043 * 044 * <p> 045 * The purpose of this class is: 046 * <ul> 047 * <li>Maintain a read-only configuration state of a serializer. 048 * <li>Create session objects used for serializing POJOs (i.e. {@link SerializerSession}). 049 * <li>Provide convenience methods for serializing POJOs without having to construct session objects. 050 * </ul> 051 * 052 * <p> 053 * Subclasses should (but are not required to) extend directly from {@link OutputStreamSerializer} or {@link WriterSerializer} depending on 054 * whether it's a stream or character based serializer. 055 * 056 * <p> 057 * Subclasses must implement serialization via one of the following methods: 058 * <ul class='javatree'> 059 * <li class='jmp'>{@link #doWrite(SerializerSession, SerializerPipe, Object)} 060 * <li class='jmp'>{@link SerializerSession#doWrite(SerializerPipe, Object)} 061 * </ul> 062 * <br> 063 * 064 * <h5 class='section'>Notes:</h5><ul> 065 * <li class='note'>This class is thread safe and reusable. 066 * </ul> 067 * 068 * <h5 class='section'>See Also:</h5><ul> 069 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a> 070 071 * </ul> 072 */ 073@SuppressWarnings({ 074 "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants 075 "rawtypes" // createSerializerBuilder() returns the raw Builder because the concrete SELF type is only known reflectively via Context.createBuilder(c) 076}) 077public class Serializer extends MarshallingTraverseContext { 078 079 /** 080 * Builder class. 081 */ 082 @SuppressWarnings({ 083 "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name. 084 }) 085 public abstract static class Builder<SELF extends Builder<SELF>> extends MarshallingTraverseContext.Builder<SELF> { 086 087 private boolean addBeanTypes; 088 private boolean addRootType; 089 private boolean keepNullProperties; 090 private boolean nonDefault; 091 private boolean sortCollections; 092 private boolean sortMaps; 093 private boolean trimEmptyCollections; 094 private boolean trimEmptyMaps; 095 private boolean trimStrings; 096 private Class<? extends SerializerListener> listener; 097 private String accept; 098 private String produces; 099 private UriContext uriContext; 100 private UriRelativity uriRelativity; 101 private UriResolution uriResolution; 102 103 /** 104 * Constructor, default settings. 105 */ 106 protected Builder() { 107 produces = null; 108 accept = null; 109 addBeanTypes = env("Serializer.addBeanTypes", false); 110 addRootType = env("Serializer.addRootType", false); 111 keepNullProperties = env("Serializer.keepNullProperties", false); 112 nonDefault = env("Serializer.nonDefault", false); 113 sortCollections = env("Serializer.sortCollections", false); 114 sortMaps = env("Serializer.sortMaps", false); 115 trimEmptyCollections = env("Serializer.trimEmptyCollections", false); 116 trimEmptyMaps = env("Serializer.trimEmptyMaps", false); 117 trimStrings = env("Serializer.trimStrings", false); 118 uriContext = UriContext.DEFAULT; 119 uriRelativity = UriRelativity.RESOURCE; 120 uriResolution = UriResolution.NONE; 121 listener = null; 122 } 123 124 /** 125 * Copy constructor. 126 * 127 * @param copyFrom The builder to copy from. 128 * <br>Cannot be <jk>null</jk>. 129 */ 130 protected Builder(Builder<?> copyFrom) { 131 super(reqnn("copyFrom", copyFrom)); 132 produces = copyFrom.produces; 133 accept = copyFrom.accept; 134 addBeanTypes = copyFrom.addBeanTypes; 135 addRootType = copyFrom.addRootType; 136 keepNullProperties = copyFrom.keepNullProperties; 137 nonDefault = copyFrom.nonDefault; 138 sortCollections = copyFrom.sortCollections; 139 sortMaps = copyFrom.sortMaps; 140 trimEmptyCollections = copyFrom.trimEmptyCollections; 141 trimEmptyMaps = copyFrom.trimEmptyMaps; 142 trimStrings = copyFrom.trimStrings; 143 uriContext = copyFrom.uriContext; 144 uriRelativity = copyFrom.uriRelativity; 145 uriResolution = copyFrom.uriResolution; 146 listener = copyFrom.listener; 147 } 148 149 /** 150 * Copy constructor. 151 * 152 * @param copyFrom The bean to copy from. 153 * <br>Cannot be <jk>null</jk>. 154 */ 155 protected Builder(Serializer copyFrom) { 156 super(reqnn("copyFrom", copyFrom)); 157 produces = copyFrom.produces; 158 accept = copyFrom.accept; 159 addBeanTypes = copyFrom.addBeanTypes; 160 addRootType = copyFrom.addRootType; 161 keepNullProperties = copyFrom.keepNullProperties; 162 nonDefault = copyFrom.nonDefault; 163 sortCollections = copyFrom.sortCollections; 164 sortMaps = copyFrom.sortMaps; 165 trimEmptyCollections = copyFrom.trimEmptyCollections; 166 trimEmptyMaps = copyFrom.trimEmptyMaps; 167 trimStrings = copyFrom.trimStrings; 168 uriContext = copyFrom.getUriContext(); 169 uriRelativity = copyFrom.getUriRelativity(); 170 uriResolution = copyFrom.getUriResolution(); 171 listener = copyFrom.listener; 172 } 173 174 /** 175 * Specifies the accept media types that the serializer can handle. 176 * 177 * <p> 178 * Can contain meta-characters per the <c>media-type</c> specification of <a class="doclink" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">RFC2616/14.1</a> 179 * <p> 180 * If empty, then assumes the only media type supported is <c>produces</c>. 181 * <p> 182 * For example, if this serializer produces <js>"application/json"</js> but should handle media types of 183 * <js>"application/json"</js> and <js>"text/json"</js>, then the arguments should be: 184 * <p class='bjava'> 185 * <jv>builder</jv>.produces(<js>"application/json"</js>); 186 * <jv>builder</jv>.accept(<js>"application/json,text/json"</js>); 187 * </p> 188 * <p> 189 * The accept value can also contain q-values. 190 * 191 * @param value The value for this setting. 192 * <br>Can be <jk>null</jk> (will default to the value specified by {@link #produces(String)}). 193 * @return This object. 194 */ 195 public SELF accept(String value) { 196 accept = value; 197 return self(); 198 } 199 200 /** 201 * Add <js>"_type"</js> properties when needed. 202 * 203 * <p> 204 * When enabled, <js>"_type"</js> properties will be added to beans if their type cannot be inferred 205 * through reflection. 206 * 207 * <p> 208 * This is used to recreate the correct objects during parsing if the object types cannot be inferred. 209 * <br>For example, when serializing a <c>Map<String,Object></c> field where the bean class cannot be determined from 210 * the type of the values. 211 * 212 * <p> 213 * Note the differences between the following settings: 214 * <ul class='javatree'> 215 * <li class='jf'>{@link #addRootType()} - Affects whether <js>'_type'</js> is added to root node. 216 * <li class='jf'>{@link #addBeanTypes()} - Affects whether <js>'_type'</js> is added to any nodes. 217 * </ul> 218 * 219 * <h5 class='section'>Example:</h5> 220 * <p class='bjava'> 221 * <jc>// Create a serializer that adds _type to nodes.</jc> 222 * WriterSerializer <jv>serializer</jv> = JsonSerializer 223 * .<jsm>create</jsm>() 224 * .addBeanTypes() 225 * .build(); 226 * 227 * <jc>// Our map of beans to serialize.</jc> 228 * <ja>@Marshalled</ja>(typeName=<js>"mybean"</js>) 229 * <jk>public class</jk> MyBean { 230 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 231 * } 232 * JsonMap <jv>myMap</jv> = JsonMap.of(<js>"foo"</js>, <jk>new</jk> MyBean()); 233 * 234 * <jc>// Will contain: {"foo":{"_type":"mybean","foo":"bar"}}</jc> 235 * String <jv>json</jv> = <jv>serializer</jv>.write(<jv>myMap</jv>); 236 * </p> 237 * 238 * <p> 239 * <b>Token streams:</b> the cursor's raw structural methods do not emit <c>"_type"</c>; 240 * however this setting IS honored by 241 * {@link TokenWriter#object(Object) TokenWriter.object(Object)} 242 * when walking a bean. 243 * 244 * @return This object. 245 */ 246 public SELF addBeanTypes() { 247 return addBeanTypes(true); 248 } 249 250 /** 251 * Same as {@link #addBeanTypes()} but allows you to explicitly specify the value. 252 * 253 * @param value The value for this setting. 254 * @return This object. 255 */ 256 public SELF addBeanTypes(boolean value) { 257 addBeanTypes = value; 258 return self(); 259 } 260 261 /** 262 * Add type attribute to root nodes. 263 * 264 * <p> 265 * When enabled, <js>"_type"</js> properties will be added to top-level beans. 266 * 267 * <p> 268 * When disabled, it is assumed that the parser knows the exact Java POJO type being parsed, and therefore top-level 269 * type information that might normally be included to determine the data type will not be serialized. 270 * 271 * <p> 272 * For example, when serializing a top-level POJO with a {@link Marshalled#typeName() @Marshalled(typeName)} value, a 273 * <js>'_type'</js> attribute will only be added when this setting is enabled. 274 * 275 * <p> 276 * Note the differences between the following settings: 277 * <ul class='javatree'> 278 * <li class='jf'>{@link #addRootType()} - Affects whether <js>'_type'</js> is added to root node. 279 * <li class='jf'>{@link #addBeanTypes()} - Affects whether <js>'_type'</js> is added to any nodes. 280 * </ul> 281 * 282 * <h5 class='section'>Example:</h5> 283 * <p class='bjava'> 284 * <jc>// Create a serializer that adds _type to root node.</jc> 285 * WriterSerializer <jv>serializer</jv>= JsonSerializer 286 * .<jsm>create</jsm>() 287 * .addRootType() 288 * .build(); 289 * 290 * <jc>// Our bean to serialize.</jc> 291 * <ja>@Marshalled</ja>(typeName=<js>"mybean"</js>) 292 * <jk>public class</jk> MyBean { 293 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 294 * } 295 * 296 * <jc>// Will contain: {"_type":"mybean","foo":"bar"}</jc> 297 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 298 * </p> 299 * 300 * <p> 301 * <b>Token streams:</b> the cursor's raw structural methods do not emit <c>"_type"</c>; 302 * however this setting IS honored by 303 * {@link TokenWriter#object(Object) TokenWriter.object(Object)} 304 * for the root value. 305 * 306 * @return This object. 307 */ 308 public SELF addRootType() { 309 return addRootType(true); 310 } 311 312 /** 313 * Same as {@link #addRootType()} but allows you to explicitly specify the value. 314 * 315 * @param value The value for this setting. 316 * @return This object. 317 */ 318 public SELF addRootType(boolean value) { 319 addRootType = value; 320 return self(); 321 } 322 323 @Override /* Overridden from Context.Builder<?> */ 324 public Serializer build() { 325 return build(Serializer.class); 326 } 327 328 @Override /* Overridden from Context.Builder<?> */ 329 public abstract SELF copy(); 330 331 /** 332 * Returns the current value for the 'accept' property. 333 * 334 * @return The current value for the 'accept' property. 335 */ 336 public String getAccept() { return accept; } 337 338 /** 339 * Returns the current value for the 'produces' property. 340 * 341 * @return The current value for the 'produces' property. 342 */ 343 public String getProduces() { return produces; } 344 345 @Override /* Overridden from Context.Builder<?> */ 346 public HashKey hashKey() { 347 // @formatter:off 348 return HashKey.of( 349 super.hashKey(), 350 produces, 351 accept, 352 addBeanTypes, 353 addRootType, 354 keepNullProperties, 355 nonDefault, 356 sortCollections, 357 sortMaps, 358 trimEmptyCollections, 359 trimEmptyMaps, 360 trimStrings, 361 uriContext, 362 uriRelativity, 363 uriResolution, 364 listener 365 ); 366 // @formatter:on 367 } 368 369 /** 370 * Don't trim null bean property values. 371 * 372 * <p> 373 * When enabled, null bean values will be serialized to the output. 374 * 375 * <h5 class='section'>Notes:</h5><ul> 376 * <li class='note'>Not enabling this setting will cause <c>Map</c>s with <jk>null</jk> values to be lost during parsing. 377 * </ul> 378 * 379 * <h5 class='section'>Example:</h5> 380 * <p class='bjava'> 381 * <jc>// Create a serializer that serializes null properties.</jc> 382 * WriterSerializer <jv>serializer</jv> = JsonSerializer 383 * .<jsm>create</jsm>() 384 * .keepNullProperties() 385 * .build(); 386 * 387 * <jc>// Our bean to serialize.</jc> 388 * <jk>public class</jk> MyBean { 389 * <jk>public</jk> String <jf>foo</jf> = <jk>null</jk>; 390 * } 391 * 392 * <jc>// Will contain "{foo:null}".</jc> 393 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 394 * </p> 395 * 396 * <p> 397 * <b>Token streams:</b> the cursor's raw structural methods always emit what the caller 398 * asks for; however this setting IS honored by 399 * {@link TokenWriter#object(Object) TokenWriter.object(Object)} 400 * during a bean walk. 401 * 402 * @return This object. 403 */ 404 public SELF keepNullProperties() { 405 return keepNullProperties(true); 406 } 407 408 /** 409 * Same as {@link #keepNullProperties()} but allows you to explicitly specify the value. 410 * 411 * @param value The value for this setting. 412 * @return This object. 413 */ 414 public SELF keepNullProperties(boolean value) { 415 keepNullProperties = value; 416 return self(); 417 } 418 419 /** 420 * Don't serialize bean properties whose values equal the default for their type. 421 * 422 * <p> 423 * When enabled, bean properties whose current value equals the type's natural default are omitted from 424 * the serialized output. This is the Juneau analog of Jackson's <c>JsonInclude.Include.NON_DEFAULT</c> 425 * inclusion mode. 426 * 427 * <p> 428 * The notion of "default" depends on the property type: 429 * <ul class='spaced-list'> 430 * <li>Primitives and primitive wrappers (<code><jk>int</jk></code>, <code>Integer</code>, 431 * <code><jk>boolean</jk></code>, <code>Boolean</code>, etc.) use the Java type default 432 * (<c>0</c>, <c>false</c>, <code>'\0'</code>, <jk>null</jk>). 433 * <li>Bean properties on bean-typed property values use a <i>reference instance</i> of the bean type — 434 * instantiated once via the no-arg constructor and cached on the {@link ClassMeta} — and the 435 * property is omitted when its current value equals that reference's value. 436 * <li>When no reference instance can be built (no accessible no-arg constructor, or instantiation 437 * throws), <c>nonDefault</c> is silently skipped for that bean — the property is emitted normally 438 * rather than failing the serialize. 439 * </ul> 440 * 441 * <p> 442 * <b>Equality semantics:</b> value-equals-default uses {@link Objects#equals}, with numeric values 443 * compared by mathematical value (BigDecimal.compareTo-style) so <c>1.0</c> equals <c>1</c>, 444 * <c>0.0</c> equals <c>-0.0</c>, and <code>BigDecimal(<js>"1.0"</js>)</code> equals 445 * <code>BigDecimal(<js>"1"</js>)</code>. 446 * 447 * <p> 448 * <b>Precedence:</b> the serialize-side inclusion knobs short-circuit in a fixed order — first "omit" wins: 449 * <ol> 450 * <li><jk>null</jk> values (including {@link Optional#empty()}) are governed by 451 * {@link #keepNullProperties()}. 452 * <li>Non-<jk>null</jk> string values are normalized by {@link #trimStrings()}. 453 * <li>Empty collections / arrays / maps are omitted by {@link #trimEmptyCollections()} / 454 * {@link #trimEmptyMaps()}. 455 * <li>Surviving values that equal their type's default are omitted by <c>nonDefault</c>. 456 * </ol> 457 * <jk>null</jk> is resolved by <c>keepNullProperties</c>, not treated as a <c>nonDefault</c> case. 458 * 459 * <h5 class='section'>Example:</h5> 460 * <p class='bjava'> 461 * <jc>// Create a serializer that omits default-valued properties.</jc> 462 * WriterSerializer <jv>serializer</jv> = JsonSerializer 463 * .<jsm>create</jsm>() 464 * .nonDefault() 465 * .build(); 466 * 467 * <jc>// Our bean to serialize.</jc> 468 * <jk>public class</jk> MyBean { 469 * <jk>public int</jk> <jf>count</jf>; <jc>// Default 0; omitted unless set to non-zero.</jc> 470 * <jk>public</jk> String <jf>name</jf> = <js>"x"</js>; <jc>// Bean-default is "x"; only omitted when value equals "x".</jc> 471 * } 472 * 473 * <jc>// Produces "{}" because both fields equal their defaults.</jc> 474 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 475 * </p> 476 * 477 * <h5 class='section'>See Also:</h5><ul> 478 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/NullAndInclusionPolicies">Null & Inclusion Policies</a> 479 * </ul> 480 * 481 * @return This object. 482 * @since 10.0.0 483 */ 484 public SELF nonDefault() { 485 return nonDefault(true); 486 } 487 488 /** 489 * Same as {@link #nonDefault()} but allows you to explicitly specify the value. 490 * 491 * @param value The value for this setting. 492 * @return This object. 493 * @since 10.0.0 494 */ 495 public SELF nonDefault(boolean value) { 496 nonDefault = value; 497 return self(); 498 } 499 500 /** 501 * Serializer listener. 502 * 503 * <p> 504 * Class used to listen for errors and warnings that occur during serialization. 505 * 506 * <h5 class='section'>Example:</h5> 507 * <p class='bjava'> 508 * <jc>// Define our serializer listener.</jc> 509 * <jc>// Simply captures all errors.</jc> 510 * <jk>public class</jk> MySerializerListener <jk>extends</jk> SerializerListener { 511 * 512 * <jc>// A simple property to store our events.</jc> 513 * <jk>public</jk> List<String> <jf>events</jf> = <jk>new</jk> LinkedList<>(); 514 * 515 * <ja>@Override</ja> 516 * <jk>public</jk> <T> <jk>void</jk> onError(SerializerSession <jv>session</jv>, Throwable <jv>throwable</jv>, String <jv>msg</jv>) { 517 * <jf>events</jf>.add(<jv>session</jv>.getLastLocation() + <js>","</js> + <jv>msg</jv> + <js>","</js> + <jv>throwable</jv>); 518 * } 519 * } 520 * 521 * <jc>// Create a serializer using our listener.</jc> 522 * WriterSerializer <jv>serializer</jv> = JsonSerializer 523 * .<jsm>create</jsm>() 524 * .listener(MySerializerListener.<jk>class</jk>) 525 * .build(); 526 * 527 * <jc>// Create a session object.</jc> 528 * <jc>// Needed because listeners are created per-session.</jc> 529 * <jk>try</jk> (WriterSerializerSession <jv>session</jv> = <jv>serializer</jv>.createSession()) { 530 * 531 * <jc>// Serialize a bean.</jc> 532 * String <jv>json</jv> = <jv>session</jv>.write(<jk>new</jk> MyBean()); 533 * 534 * <jc>// Get the listener.</jc> 535 * MySerializerListener <jv>listener</jv> = <jv>session</jv>.getListener(MySerializerListener.<jk>class</jk>); 536 * 537 * <jc>// Dump the results to the console.</jc> 538 * Json5.<jsf>DEFAULT</jsf>.println(<jv>listener</jv>.<jf>events</jf>); 539 * } 540 * </p> 541 * 542 * <p> 543 * <b>Token streams:</b> this setting is not honored by {@link TokenWritable#writeTokens(Object) writeTokens} 544 * — listeners are POJO-level observers with no token-layer analog. 545 * 546 * @param value 547 * The new value for this property. 548 * <br>Can be <jk>null</jk> (no listener will be used, listener methods will not be called). 549 * @return This object. 550 */ 551 public SELF listener(Class<? extends SerializerListener> value) { 552 listener = value; 553 return self(); 554 } 555 556 /** 557 * Specifies the media type that this serializer produces. 558 * 559 * @param value The value for this setting. 560 * <br>Can be <jk>null</jk> (clears the produces media type). 561 * @return This object. 562 */ 563 public SELF produces(String value) { 564 produces = value; 565 return self(); 566 } 567 568 /** 569 * Sort arrays and collections alphabetically. 570 * 571 * <p> 572 * When enabled, copies and sorts the contents of arrays and collections before serializing them. 573 * 574 * <p> 575 * Note that this introduces a performance penalty since it requires copying the existing collection. 576 * 577 * <h5 class='section'>Example:</h5> 578 * <p class='bjava'> 579 * <jc>// Create a serializer that sorts arrays and collections before serialization.</jc> 580 * WriterSerializer <jv>serializer</jv> = JsonSerializer 581 * .<jsm>create</jsm>() 582 * .sortCollections() 583 * .build(); 584 * 585 * <jc>// An unsorted array</jc> 586 * String[] <jv>myArray</jv> = {<js>"foo"</js>,<js>"bar"</js>,<js>"baz"</js>}; 587 * 588 * <jc>// Produces ["bar","baz","foo"]</jc> 589 * String <jv>json</jv> = <jv>serializer</jv>.write(<jv>myArray</jv>); 590 * </p> 591 * 592 * <p> 593 * <b>Token streams:</b> the cursor's raw structural methods emit elements in the order the 594 * caller emits them. This setting IS honored by 595 * {@link TokenWriter#object(Object) TokenWriter.object(Object)} 596 * when walking a {@link Collection} or array. 597 * 598 * @return This object. 599 */ 600 public SELF sortCollections() { 601 return sortCollections(true); 602 } 603 604 /** 605 * Same as {@link #sortCollections()} but allows you to explicitly specify the value. 606 * 607 * @param value The value for this setting. 608 * @return This object. 609 */ 610 public SELF sortCollections(boolean value) { 611 sortCollections = value; 612 return self(); 613 } 614 615 /** 616 * Sort maps alphabetically. 617 * 618 * <p> 619 * When enabled, copies and sorts the contents of maps by their keys before serializing them. 620 * 621 * <p> 622 * Note that this introduces a performance penalty. 623 * 624 * <h5 class='section'>Example:</h5> 625 * <p class='bjava'> 626 * <jc>// Create a serializer that sorts maps before serialization.</jc> 627 * WriterSerializer <jv>serializer</jv> = JsonSerializer 628 * .<jsm>create</jsm>() 629 * .sortMaps() 630 * .build(); 631 * 632 * <jc>// An unsorted map.</jc> 633 * JsonMap <jv>myMap</jv> = JsonMap.<jsm>of</jsm>(<js>"foo"</js>,1,<js>"bar"</js>,2,<js>"baz"</js>,3); 634 * 635 * <jc>// Produces {"bar":2,"baz":3,"foo":1}</jc> 636 * String <jv>json</jv> = <jv>serializer</jv>.write(<jv>myMap</jv>); 637 * </p> 638 * 639 * <p> 640 * <b>Token streams:</b> the cursor's raw structural methods emit map / bean entries in the 641 * order the caller emits them. This setting IS honored by 642 * {@link TokenWriter#object(Object) TokenWriter.object(Object)} 643 * when walking a {@link Map} or bean. 644 * 645 * @return This object. 646 */ 647 public SELF sortMaps() { 648 return sortMaps(true); 649 } 650 651 /** 652 * Same as {@link #sortMaps()} but allows you to explicitly specify the value. 653 * 654 * @param value The value for this setting. 655 * @return This object. 656 */ 657 public SELF sortMaps(boolean value) { 658 sortMaps = value; 659 return self(); 660 } 661 662 /** 663 * Trim empty lists and arrays. 664 * 665 * <p> 666 * When enabled, empty lists and arrays will not be serialized. 667 * 668 * <p> 669 * Note that enabling this setting has the following effects on parsing: 670 * <ul class='spaced-list'> 671 * <li> 672 * Map entries with empty list values will be lost. 673 * <li> 674 * Bean properties with empty list values will not be set. 675 * </ul> 676 * 677 * <h5 class='section'>Example:</h5> 678 * <p class='bjava'> 679 * <jc>// Create a serializer that skips empty arrays and collections.</jc> 680 * WriterSerializer <jv>serializer</jv> = JsonSerializer 681 * .<jsm>create</jsm>() 682 * .trimEmptyCollections() 683 * .build(); 684 * 685 * <jc>// A bean with a field with an empty array.</jc> 686 * <jk>public class</jk> MyBean { 687 * <jk>public</jk> String[] <jf>foo</jf> = {}; 688 * } 689 * 690 * <jc>// Produces {}</jc> 691 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 692 * </p> 693 * 694 * <p> 695 * <b>Token streams:</b> the cursor's raw structural methods emit what the caller asks for. 696 * This setting IS honored by 697 * {@link TokenWriter#object(Object) TokenWriter.object(Object)} 698 * during a bean walk — bean / map fields whose value is an empty collection or array 699 * are skipped. 700 * 701 * @return This object. 702 */ 703 public SELF trimEmptyCollections() { 704 return trimEmptyCollections(true); 705 } 706 707 /** 708 * Same as {@link #trimEmptyCollections()} but allows you to explicitly specify the value. 709 * 710 * @param value The value for this setting. 711 * @return This object. 712 */ 713 public SELF trimEmptyCollections(boolean value) { 714 trimEmptyCollections = value; 715 return self(); 716 } 717 718 /** 719 * Trim empty maps. 720 * 721 * <p> 722 * When enabled, empty map values will not be serialized to the output. 723 * 724 * <p> 725 * Note that enabling this setting has the following effects on parsing: 726 * <ul class='spaced-list'> 727 * <li> 728 * Bean properties with empty map values will not be set. 729 * </ul> 730 * 731 * <h5 class='section'>Example:</h5> 732 * <p class='bjava'> 733 * <jc>// Create a serializer that skips empty maps.</jc> 734 * WriterSerializer <jv>serializer</jv> = JsonSerializer 735 * .<jsm>create</jsm>() 736 * .trimEmptyMaps() 737 * .build(); 738 * 739 * <jc>// A bean with a field with an empty map.</jc> 740 * <jk>public class</jk> MyBean { 741 * <jk>public</jk> JsonMap <jf>foo</jf> = JsonMap.<jsm>of</jsm>(); 742 * } 743 * 744 * <jc>// Produces {}</jc> 745 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 746 * </p> 747 * 748 * <p> 749 * <b>Token streams:</b> the cursor's raw structural methods emit what the caller asks for. 750 * This setting IS honored by 751 * {@link TokenWriter#object(Object) TokenWriter.object(Object)} 752 * during a bean walk — bean / map fields whose value is an empty map are skipped. 753 * 754 * @return This object. 755 */ 756 public SELF trimEmptyMaps() { 757 return trimEmptyMaps(true); 758 } 759 760 /** 761 * Same as {@link #trimEmptyMaps()} but allows you to explicitly specify the value. 762 * 763 * @param value The value for this setting. 764 * @return This object. 765 */ 766 public SELF trimEmptyMaps(boolean value) { 767 trimEmptyMaps = value; 768 return self(); 769 } 770 771 /** 772 * Trim strings. 773 * 774 * <p> 775 * When enabled, string values will be trimmed of whitespace using {@link String#trim()} before being serialized. 776 * 777 * <h5 class='section'>Example:</h5> 778 * <p class='bjava'> 779 * <jc>// Create a serializer that trims strings before serialization.</jc> 780 * WriterSerializer <jv>serializer</jv> = JsonSerializer 781 * .<jsm>create</jsm>() 782 * .trimStrings() 783 * .build(); 784 * 785 * <jc>// A map with space-padded keys/values</jc> 786 * JsonMap <jv>myMap</jv> = JsonMap.<jsm>of</jsm>(<js>" foo "</js>, <js>" bar "</js>); 787 * 788 * <jc>// Produces "{foo:'bar'}"</jc> 789 * String <jv>json</jv> = <jv>serializer</jv>.toString(<jv>myMap</jv>); 790 * </p> 791 * 792 * @return This object. 793 */ 794 public SELF trimStrings() { 795 return trimStrings(true); 796 } 797 798 /** 799 * Same as {@link #trimStrings()} but allows you to explicitly specify the value. 800 * 801 * @param value The value for this setting. 802 * @return This object. 803 */ 804 public SELF trimStrings(boolean value) { 805 trimStrings = value; 806 return self(); 807 } 808 809 /** 810 * URI context bean. 811 * 812 * <p> 813 * Bean used for resolution of URIs to absolute or root-relative form. 814 * 815 * <h5 class='section'>Example:</h5> 816 * <p class='bjava'> 817 * <jc>// Our URI contextual information.</jc> 818 * String <jv>authority</jv> = <js>"http://localhost:10000"</js>; 819 * String <jv>contextRoot</jv> = <js>"/myContext"</js>; 820 * String <jv>servletPath</jv> = <js>"/myServlet"</js>; 821 * String <jv>pathInfo</jv> = <js>"/foo"</js>; 822 * 823 * <jc>// Create a UriContext object.</jc> 824 * UriContext <jv>uriContext</jv> = <jk>new</jk> UriContext(<jv>authority</jv>, <jv>contextRoot</jv>, <jv>servletPath</jv>, <jv>pathInfo</jv>); 825 * 826 * <jc>// Associate it with our serializer.</jc> 827 * WriterSerializer <jv>serializer</jv> = JsonSerializer 828 * .<jsm>create</jsm>() 829 * .uriContext(<jv>uriContext</jv>) 830 * .uriRelativity(<jsf>RESOURCE</jsf>) <jc>// Assume relative paths are relative to servlet.</jc> 831 * .uriResolution(<jsf>ABSOLUTE</jsf>) <jc>// Serialize URLs as absolute paths.</jc> 832 * .build(); 833 * 834 * <jc>// A relative URL</jc> 835 * URL <jv>myUrl</jv> = <jk>new</jk> URL(<js>"bar"</js>); 836 * 837 * <jc>// Produces "http://localhost:10000/myContext/myServlet/foo/bar"</jc> 838 * String <jv>json</jv> = <jv>serializer</jv>.toString(<jv>myUrl</jv>); 839 * </p> 840 * 841 * <h5 class='section'>See Also:</h5><ul> 842 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/MarshallingUris">URIs</a> 843 * </ul> 844 * 845 * <p> 846 * <b>Token streams:</b> this setting is not honored by {@link TokenWritable#writeTokens(Object) writeTokens} 847 * — URI rewriting is a databind concern; the token layer emits string values verbatim. 848 * 849 * @param value The new value for this property. 850 * <br>Can be <jk>null</jk> (defaults to <c>UriContext.DEFAULT</c>). 851 * @return This object. 852 */ 853 public SELF uriContext(UriContext value) { 854 uriContext = value; 855 return self(); 856 } 857 858 /** 859 * URI relativity. 860 * 861 * <p> 862 * Defines what relative URIs are relative to when serializing any of the following: 863 * <ul> 864 * <li>{@link URI} 865 * <li>{@link URL} 866 * <li>Properties and classes annotated with {@link Uri @Uri} 867 * </ul> 868 * 869 * <p> 870 * See {@link #uriContext(UriContext)} for examples. 871 * 872 * <ul class='values javatree'> 873 * <li class='jf'>{@link UriRelativity#RESOURCE} 874 * - Relative URIs should be considered relative to the servlet URI. 875 * <li class='jf'>{@link UriRelativity#PATH_INFO} 876 * - Relative URIs should be considered relative to the request URI. 877 * </ul> 878 * 879 * <h5 class='section'>See Also:</h5><ul> 880 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/MarshallingUris">URIs</a> 881 * </ul> 882 * 883 * <p> 884 * <b>Token streams:</b> this setting is not honored by {@link TokenWritable#writeTokens(Object) writeTokens} 885 * — URI rewriting is a databind concern. 886 * 887 * @param value 888 * The new value for this property. 889 * <br>The default is {@link UriRelativity#RESOURCE} 890 * <br>Can be <jk>null</jk> (defaults to <c>UriRelativity.RESOURCE</c>). 891 * @return This object. 892 */ 893 public SELF uriRelativity(UriRelativity value) { 894 uriRelativity = value; 895 return self(); 896 } 897 898 /** 899 * URI resolution. 900 * 901 * <p> 902 * Defines the resolution level for URIs when serializing any of the following: 903 * <ul> 904 * <li>{@link URI} 905 * <li>{@link URL} 906 * <li>Properties and classes annotated with {@link Uri @Uri} 907 * </ul> 908 * 909 * <p> 910 * See {@link #uriContext(UriContext)} for examples. 911 * 912 * <ul class='values javatree'> 913 * <li class='jf'>{@link UriResolution#ABSOLUTE} 914 * - Resolve to an absolute URL (e.g. <js>"http://host:port/context-root/servlet-path/path-info"</js>). 915 * <li class='jf'>{@link UriResolution#ROOT_RELATIVE} 916 * - Resolve to a root-relative URL (e.g. <js>"/context-root/servlet-path/path-info"</js>). 917 * <li class='jf'>{@link UriResolution#NONE} 918 * - Don't do any URL resolution. 919 * </ul> 920 * 921 * <h5 class='section'>See Also:</h5><ul> 922 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/MarshallingUris">URIs</a> 923 * </ul> 924 * 925 * <p> 926 * <b>Token streams:</b> this setting is not honored by {@link TokenWritable#writeTokens(Object) writeTokens} 927 * — URI rewriting is a databind concern. 928 * 929 * @param value 930 * The new value for this property. 931 * <br>The default is {@link UriResolution#NONE} 932 * <br>Can be <jk>null</jk> (defaults to <c>UriResolution.NONE</c>). 933 * @return This object. 934 */ 935 public SELF uriResolution(UriResolution value) { 936 uriResolution = value; 937 return self(); 938 } 939 940 941 } 942 943 /** 944 * Concrete default builder leaf for the non-subclassed {@link Serializer#create()} / {@link Serializer#copy()} path. 945 */ 946 public static final class DefaultBuilder extends Builder<DefaultBuilder> { 947 948 DefaultBuilder() {} 949 950 DefaultBuilder(Serializer copyFrom) { 951 super(copyFrom); 952 } 953 954 DefaultBuilder(Builder<?> copyFrom) { 955 super(copyFrom); 956 } 957 958 @Override /* Overridden from Context.Builder<?> */ 959 public DefaultBuilder copy() { 960 return new DefaultBuilder(this); 961 } 962 } 963 964 /** 965 * Represents no Serializer. 966 */ 967 public abstract static class Null extends Serializer { 968 @SuppressWarnings({ 969 "java:S1186" // Constructor required by Serializer parent class, even though Null is abstract and never instantiated directly 970 }) 971 private Null(Builder<?> builder) { 972 super(builder); 973 } 974 } 975 976 /** 977 * Creates a new builder for this object. 978 * 979 * @return A new builder. 980 */ 981 @SuppressWarnings({ 982 "java:S1452" // Self-typed builder: Builder<?> is the only non-raw, leaf-free return; chaining + build() unaffected. 983 }) 984 public static Builder<?> create() { 985 return new DefaultBuilder(); 986 } 987 988 /** 989 * Instantiates a builder of the specified serializer class. 990 * 991 * <p> 992 * Looks for a public static method called <c>create</c> that returns an object that can be passed into a public 993 * or protected constructor of the class. 994 * 995 * @param c The serializer class to create a builder for. 996 * <br>Must not be <jk>null</jk>. 997 * @return A new builder. 998 */ 999 public static Builder createSerializerBuilder(Class<? extends Serializer> c) { 1000 return (Builder)Context.createBuilder(c); 1001 } 1002 1003 protected final boolean addBeanTypes; 1004 protected final boolean addRootType; 1005 protected final boolean keepNullProperties; 1006 protected final boolean nonDefault; 1007 protected final boolean sortCollections; 1008 protected final boolean sortMaps; 1009 protected final boolean trimEmptyCollections; 1010 protected final boolean trimEmptyMaps; 1011 protected final boolean trimStrings; 1012 protected final Class<? extends SerializerListener> listener; 1013 protected final String accept; 1014 protected final String produces; 1015 private final UriContext uriContext; 1016 private final UriRelativity uriRelativity; 1017 private final UriResolution uriResolution; 1018 private final MediaRanges acceptRanges; 1019 private final List<MediaType> acceptMediaTypes; 1020 private final MediaType producesMediaType; 1021 1022 /** 1023 * Constructor 1024 * 1025 * @param builder The builder for this object. 1026 * <br>Cannot be <jk>null</jk>. 1027 */ 1028 protected Serializer(Builder<?> builder) { 1029 super(builder); 1030 1031 accept = builder.accept; 1032 addBeanTypes = builder.addBeanTypes; 1033 addRootType = builder.addRootType; 1034 keepNullProperties = builder.keepNullProperties; 1035 listener = builder.listener; 1036 nonDefault = builder.nonDefault; 1037 produces = builder.produces; 1038 sortCollections = builder.sortCollections; 1039 sortMaps = builder.sortMaps; 1040 trimEmptyCollections = builder.trimEmptyCollections; 1041 trimEmptyMaps = builder.trimEmptyMaps; 1042 trimStrings = builder.trimStrings; 1043 uriContext = builder.uriContext; 1044 uriRelativity = builder.uriRelativity; 1045 uriResolution = builder.uriResolution; 1046 1047 this.producesMediaType = MediaType.of(produces); 1048 this.acceptRanges = MediaRanges.of(nn(accept) ? accept : produces); 1049 this.acceptMediaTypes = u(l(nn(builder.accept) ? MediaType.ofAll(splita(builder.accept)) : new MediaType[]{this.producesMediaType})); 1050 } 1051 1052 @Override /* Overridden from Context */ 1053 public Builder<?> copy() { 1054 return new DefaultBuilder(this); 1055 } 1056 1057 @Override /* Overridden from Context */ 1058 public SerializerSession.Builder<?> createSession() { 1059 return SerializerSession.create(this); 1060 } 1061 1062 /** 1063 * Performs an action on the media types handled based on the value of the <c>accept</c> parameter passed into the constructor. 1064 * 1065 * <p> 1066 * The order of the media types are the same as those in the <c>accept</c> parameter. 1067 * 1068 * @param action The action to perform on the media types. 1069 * <br>Must not be <jk>null</jk>. 1070 * @return This object. 1071 */ 1072 public final Serializer forEachAcceptMediaType(Consumer<MediaType> action) { 1073 for (var m : acceptMediaTypes) 1074 action.accept(m); 1075 return this; 1076 } 1077 1078 /** 1079 * Returns the media types handled based on the value of the <c>accept</c> parameter passed into the constructor. 1080 * 1081 * <p> 1082 * Note that the order of these ranges are from high to low q-value. 1083 * 1084 * @return The list of media types. Never <jk>null</jk>. 1085 */ 1086 public final MediaRanges getMediaTypeRanges() { return acceptRanges; } 1087 1088 /** 1089 * Returns the first entry in the <c>accept</c> parameter passed into the constructor. 1090 * 1091 * <p> 1092 * This signifies the 'primary' media type for this serializer. 1093 * 1094 * @return The media type. Never <jk>null</jk>. 1095 */ 1096 public final MediaType getPrimaryMediaType() { return first(acceptMediaTypes).orElseThrow(() -> new IllegalStateException("No accept media types available")); } 1097 1098 /** 1099 * Optional method that returns the response <c>Content-Type</c> for this serializer if it is different from 1100 * the matched media type. 1101 * 1102 * <p> 1103 * This method is specified to override the content type for this serializer. 1104 * For example, the {@link Json5Serializer} class returns that it handles media type 1105 * <js>"text/json5"</js>, but returns <js>"text/json"</js> as the actual content type. 1106 * This allows clients to request specific 'flavors' of content using specialized <c>Accept</c> header values. 1107 * 1108 * <p> 1109 * This method is typically meaningless if the serializer is being used stand-alone (i.e. outside of a REST server 1110 * or client). 1111 * 1112 * @return The response content type. If <jk>null</jk>, then the matched media type is used. 1113 */ 1114 public final MediaType getResponseContentType() { return producesMediaType; } 1115 1116 /** 1117 * Optional method that specifies HTTP request headers for this serializer. 1118 * 1119 * <p> 1120 * For example, {@link SoapXmlSerializer} needs to set a <c>SOAPAction</c> header. 1121 * 1122 * <p> 1123 * This method is typically meaningless if the serializer is being used stand-alone (i.e. outside of a REST server 1124 * or client). 1125 * 1126 * @param session The current session. 1127 * @return 1128 * The HTTP headers to set on HTTP requests. 1129 * Never <jk>null</jk>. 1130 */ 1131 public Map<String,String> getResponseHeaders(SerializerSession session) { 1132 return emptyMap(); 1133 } 1134 1135 @Override /* Overridden from Context */ 1136 public SerializerSession getSession() { return createSession().build(); } 1137 1138 /** 1139 * Returns <jk>true</jk> if this serializer subclasses from {@link WriterSerializer}. 1140 * 1141 * @return <jk>true</jk> if this serializer subclasses from {@link WriterSerializer}. 1142 */ 1143 public boolean isWriterSerializer() { return false; } 1144 1145 /** 1146 * Shortcut method for serializing objects directly to either a <c>String</c> or <code><jk>byte</jk>[]</code> 1147 * depending on the serializer type. 1148 * 1149 * @param o The object to serialize. 1150 * @return 1151 * The serialized object. 1152 * <br>Character-based serializers will return a <c>String</c> 1153 * <br>Stream-based serializers will return a <code><jk>byte</jk>[]</code> 1154 * @throws SerializeException If a problem occurred trying to convert the output. 1155 */ 1156 public Object write(Object o) throws SerializeException { 1157 return getSession().write(o); 1158 } 1159 1160 /** 1161 * Serializes a POJO to the specified output stream or writer. 1162 * 1163 * <p> 1164 * Equivalent to calling <c>serializer.createSession().write(o, output);</c> 1165 * 1166 * @param o The object to serialize. 1167 * @param output 1168 * The output object. 1169 * <br>Character-based serializers can handle the following output class types: 1170 * <ul> 1171 * <li>{@link Writer} 1172 * <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream. 1173 * </ul> 1174 * <br>Stream-based serializers can handle the following output class types: 1175 * <ul> 1176 * <li>{@link OutputStream} 1177 * </ul> 1178 * @throws SerializeException If a problem occurred trying to convert the output. 1179 * @throws IOException Thrown by the underlying stream. 1180 */ 1181 public final void write(Object o, Object output) throws SerializeException, IOException { 1182 getSession().write(o, output); 1183 } 1184 1185 /** 1186 * Convenience method for serializing an object to a String. 1187 * 1188 * <p> 1189 * For writer-based serializers, this is identical to calling {@link #write(Object)}. 1190 * <br>For stream-based serializers, this converts the returned byte array to a string based on 1191 * the {@link OutputStreamSerializer.Builder#binaryFormat(BinaryFormat)} setting. 1192 * 1193 * @param o The object to serialize. 1194 * @return The output serialized to a string. 1195 * @throws SerializeException If a problem occurred trying to convert the output. 1196 */ 1197 public final String writeToString(Object o) throws SerializeException { 1198 return getSession().writeToString(o); 1199 } 1200 1201 /** 1202 * Serializes a POJO to the specified pipe. 1203 * 1204 * @param session The current session. 1205 * @param pipe Where to send the output from the serializer. 1206 * @param o The object to serialize. 1207 * @throws IOException Thrown by underlying stream. 1208 * @throws SerializeException Problem occurred trying to serialize object. 1209 */ 1210 protected void doWrite(SerializerSession session, SerializerPipe pipe, Object o) throws IOException, SerializeException { 1211 throw uoex(); 1212 } 1213 1214 /** 1215 * Serializer listener. 1216 * 1217 * @see Serializer.Builder#listener(Class) 1218 * @return 1219 * Class used to listen for errors and warnings that occur during serialization. 1220 */ 1221 protected final Class<? extends SerializerListener> getListener() { return listener; } 1222 1223 /** 1224 * URI context bean. 1225 * 1226 * @see Serializer.Builder#uriContext(UriContext) 1227 * @return 1228 * Bean used for resolution of URIs to absolute or root-relative form. 1229 */ 1230 protected final UriContext getUriContext() { return uriContext; } 1231 1232 /** 1233 * URI relativity. 1234 * 1235 * @see Serializer.Builder#uriRelativity(UriRelativity) 1236 * @return 1237 * Defines what relative URIs are relative to when serializing any of the following: 1238 */ 1239 protected final UriRelativity getUriRelativity() { return uriRelativity; } 1240 1241 /** 1242 * URI resolution. 1243 * 1244 * @see Serializer.Builder#uriResolution(UriResolution) 1245 * @return 1246 * Defines the resolution level for URIs when serializing URIs. 1247 */ 1248 protected final UriResolution getUriResolution() { return uriResolution; } 1249 1250 /** 1251 * Add <js>"_type"</js> properties when needed. 1252 * 1253 * @see Serializer.Builder#addBeanTypes() 1254 * @return 1255 * <jk>true</jk> if <js>"_type"</js> properties added to beans if their type cannot be inferred 1256 * through reflection. 1257 */ 1258 protected boolean isAddBeanTypes() { return addBeanTypes; } 1259 1260 /** 1261 * Add type attribute to root nodes. 1262 * 1263 * @see Serializer.Builder#addRootType() 1264 * @return 1265 * <jk>true</jk> if type property should be added to root node. 1266 */ 1267 protected final boolean isAddRootType() { return addRootType; } 1268 1269 /** 1270 * Don't trim null bean property values. 1271 * 1272 * @see Serializer.Builder#keepNullProperties() 1273 * @return 1274 * <jk>true</jk> if null bean values are serialized to the output. 1275 */ 1276 protected final boolean isKeepNullProperties() { return keepNullProperties; } 1277 1278 /** 1279 * Don't serialize bean properties whose values equal the default for their type. 1280 * 1281 * @see Serializer.Builder#nonDefault() 1282 * @return 1283 * <jk>true</jk> if bean properties whose value equals the type / bean-constructed default are omitted. 1284 * @since 10.0.0 1285 */ 1286 protected final boolean isNonDefault() { return nonDefault; } 1287 1288 /** 1289 * Sort arrays and collections alphabetically. 1290 * 1291 * @see Serializer.Builder#sortCollections() 1292 * @return 1293 * <jk>true</jk> if arrays and collections are copied and sorted before serialization. 1294 */ 1295 protected final boolean isSortCollections() { return sortCollections; } 1296 1297 /** 1298 * Sort maps alphabetically. 1299 * 1300 * @see Serializer.Builder#sortMaps() 1301 * @return 1302 * <jk>true</jk> if maps are copied and sorted before serialization. 1303 */ 1304 protected final boolean isSortMaps() { return sortMaps; } 1305 1306 /** 1307 * Trim empty lists and arrays. 1308 * 1309 * @see Serializer.Builder#trimEmptyCollections() 1310 * @return 1311 * <jk>true</jk> if empty lists and arrays are not serialized to the output. 1312 */ 1313 protected final boolean isTrimEmptyCollections() { return trimEmptyCollections; } 1314 1315 /** 1316 * Trim empty maps. 1317 * 1318 * @see Serializer.Builder#trimEmptyMaps() 1319 * @return 1320 * <jk>true</jk> if empty map values are not serialized to the output. 1321 */ 1322 protected final boolean isTrimEmptyMaps() { return trimEmptyMaps; } 1323 1324 /** 1325 * Trim strings. 1326 * 1327 * @see Serializer.Builder#trimStrings() 1328 * @return 1329 * <jk>true</jk> if string values will be trimmed of whitespace using {@link String#trim()} before being serialized. 1330 */ 1331 protected final boolean isTrimStrings() { return trimStrings; } 1332 1333 @Override /* Overridden from MarshallingTraverseContext */ 1334 protected FluentMap<String,Object> properties() { 1335 return super.properties() 1336 .a("addBeanTypes", addBeanTypes) 1337 .a("addRootType", addRootType) 1338 .a("keepNullProperties", keepNullProperties) 1339 .a("listener", listener) 1340 .a("nonDefault", nonDefault) 1341 .a("sortCollections", sortCollections) 1342 .a("sortMaps", sortMaps) 1343 .a("trimEmptyCollections", trimEmptyCollections) 1344 .a("trimEmptyMaps", trimEmptyMaps) 1345 .a("trimStrings", trimStrings) 1346 .a("uriContext", uriContext) 1347 .a("uriRelativity", uriRelativity) 1348 .a("uriResolution", uriResolution); 1349 } 1350}