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; 018 019import static org.apache.juneau.commons.function.Suppliers.*; 020import static org.apache.juneau.commons.reflect.ReflectionUtils.*; 021import static org.apache.juneau.commons.reflect.Visibility.*; 022import static org.apache.juneau.commons.utils.ClassUtils.*; 023import static org.apache.juneau.commons.utils.CollectionUtils.*; 024import static org.apache.juneau.commons.utils.Shorts.*; 025import static org.apache.juneau.commons.utils.StringUtils.*; 026import static org.apache.juneau.commons.utils.SystemUtils.*; 027import static org.apache.juneau.commons.utils.ThrowableUtils.*; 028 029import java.beans.*; 030import java.io.*; 031import java.lang.reflect.*; 032import java.math.*; 033import java.net.*; 034import java.nio.charset.*; 035import java.time.*; 036import java.time.temporal.*; 037import java.util.*; 038import java.util.stream.*; 039 040import org.apache.juneau.commons.*; 041import org.apache.juneau.commons.bean.*; 042import org.apache.juneau.commons.collections.*; 043import org.apache.juneau.commons.conversion.*; 044import org.apache.juneau.commons.function.*; 045import org.apache.juneau.commons.http.*; 046import org.apache.juneau.commons.httppart.*; 047import org.apache.juneau.commons.inject.*; 048import org.apache.juneau.commons.reflect.*; 049import org.apache.juneau.commons.reflect.Visibility; 050import org.apache.juneau.marshall.collections.*; 051import org.apache.juneau.marshall.json5.*; 052import org.apache.juneau.marshall.marshaller.*; 053import org.apache.juneau.marshall.parser.*; 054import org.apache.juneau.marshall.serializer.*; 055import org.apache.juneau.marshall.swap.spi.*; 056import org.apache.juneau.marshall.utils.*; 057 058/** 059 * Bean context. 060 * 061 * <p> 062 * This class servers multiple purposes: 063 * <ul class='spaced-list'> 064 * <li> 065 * Provides the ability to wrap beans inside {@link Map} interfaces. 066 * <li> 067 * Serves as a repository for metadata on POJOs, such as associated {@link Bean @Marshalled} annotations, 068 * {@link PropertyNamer PropertyNamers}, etc... which are used to tailor how POJOs are serialized and parsed. 069 * </ul> 070 * 071 * <p> 072 * All serializers and parsers use this context so that they can handle POJOs using a common framework. 073 * 074 * <h5 class='topic'>Bean Contexts</h5> 075 * 076 * <p> 077 * Bean contexts are created through the {@link MarshallingContext#create() MarshallingContext.create()} and {@link Builder#build()} methods. 078 * <br>These context objects are read-only, reusable, and thread-safe. 079 * 080 * <p> 081 * Each bean context maintains a cache of {@link ClassMeta} objects that describe information about classes encountered. 082 * These <c>ClassMeta</c> objects are time-consuming to construct. 083 * Therefore, instances of {@link MarshallingContext} that share the same <js>"MarshallingContext.*"</js> property values share 084 * the same cache. This allows for efficient reuse of <c>ClassMeta</c> objects so that the information about 085 * classes only needs to be calculated once. 086 * Because of this, many of the properties defined on the {@link MarshallingContext} class cannot be overridden on the session. 087 * 088 * <h5 class='topic'>Bean Sessions</h5> 089 * 090 * <p> 091 * Whereas <c>MarshallingContext</c> objects are permanent, unchangeable, cached, and thread-safe, 092 * {@link MarshallingSession} objects are ephemeral and not thread-safe. 093 * They are meant to be used as quickly-constructed scratchpads for creating bean maps. 094 * {@link BeanMap} objects can only be created through the session. 095 * 096 * <h5 class='topic'>MarshallingContext configuration properties</h5> 097 * 098 * <p> 099 * <c>BeanContexts</c> have several configuration properties that can be used to tweak behavior on how beans are 100 * handled. These are denoted as the static <jsf>BEAN_*</jsf> fields on this class. 101 * 102 * <p> 103 * Some settings (e.g. {@link Builder#beansRequireDefaultConstructor()}) are used to differentiate between bean 104 * and non-bean classes. 105 * Attempting to create a bean map around one of these objects will throw a {@link BeanRuntimeException}. 106 * The purpose for this behavior is so that the serializers can identify these non-bean classes and convert them to 107 * plain strings using the {@link Object#toString()} method. 108 * 109 * <p> 110 * Some settings (e.g. {@link Builder#beanFieldVisibility(Visibility)}) are used to determine what kinds of properties are 111 * detected on beans. 112 * 113 * <p> 114 * Some settings (e.g. {@link Builder#beanMapPutReturnsOldValue()}) change the runtime behavior of bean maps. 115 * 116 * <h5 class='section'>Example:</h5> 117 * 118 * <p class='bjava'> 119 * <jc>// Construct a context from scratch.</jc> 120 * MarshallingContext <jv>marshallingContext</jv> = MarshallingContext 121 * .<jsm>create</jsm>() 122 * .beansRequireDefaultConstructor() 123 * .notBeanClasses(Foo.<jk>class</jk>) 124 * .build(); 125 * </p> 126 * 127 * <h5 class='topic'>Bean Maps</h5> 128 * 129 * <p> 130 * {@link BeanMap BeanMaps} are wrappers around Java beans that allow properties to be retrieved and 131 * set using the common {@link Map#put(Object,Object)} and {@link Map#get(Object)} methods. 132 * 133 * <p> 134 * Bean maps are created in two ways... 135 * <ol> 136 * <li>{@link MarshallingSession#toBeanMap(Object) MarshallingSession.toBeanMap()} - Wraps an existing bean inside a {@code Map} 137 * wrapper. 138 * <li>{@link MarshallingSession#newBeanMap(Class) MarshallingSession.newBeanMap()} - Create a new bean instance wrapped in a 139 * {@code Map} wrapper. 140 * </ol> 141 * 142 * <h5 class='section'>Example:</h5> 143 * 144 * <p class='bjava'> 145 * <jc>// A sample bean class</jc> 146 * <jk>public class</jk> Person { 147 * <jk>public</jk> String getName(); 148 * <jk>public void</jk> setName(String <jv>name</jv>); 149 * <jk>public int</jk> getAge(); 150 * <jk>public void</jk> setAge(<jk>int</jk> <jv>age</jv>); 151 * } 152 * 153 * <jc>// Create a new bean session</jc> 154 * MarshallingSession <jv>session</jv> = MarshallingContext.<jsf>DEFAULT</jsf>.createSession(); 155 * 156 * <jc>// Wrap an existing bean in a new bean map</jc> 157 * BeanMap<Person> <jv>map1</jv> = <jv>session</jv>.toBeanMap(<jk>new</jk> Person()); 158 * <jv>map1</jv>.put(<js>"name"</js>, <js>"John Smith"</js>); 159 * <jv>map1</jv>.put(<js>"age"</js>, 45); 160 * 161 * <jc>// Create a new bean instance wrapped in a new bean map</jc> 162 * BeanMap<Person> <jv>map2</jv> = <jv>session</jv>.newBeanMap(Person.<jk>class</jk>); 163 * <jv>map2</jv>.put(<js>"name"</js>, <js>"John Smith"</js>); 164 * <jv>map2</jv>.put(<js>"age"</js>, 45); 165 * Person <jv>person</jv> = <jv>map2</jv>.getBean(); <jc>// Get the bean instance that was created.</jc> 166 * </p> 167 * 168 * <h5 class='section'>Notes:</h5><ul> 169 * <li class='note'>This class is thread safe and reusable. 170 * </ul> 171 * 172 * <h5 class='section'>See Also:</h5><ul> 173 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/BeanContexts">Bean Contexts</a> 174 * </ul> 175 */ 176@SuppressWarnings({ 177 "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants 178 "java:S1452", // Wildcard required - ClassMeta<?> for parameter resolution and type variables 179 "java:S1612", // Lambdas used instead of method references for readability in complex chained expressions 180 "java:S3776", // findConversion() registers the whole conversion matrix in one method, and resolveClassMeta(Type, ...) dispatches over every Type shape 181 "java:S6539", // Monster class; MarshallingContext is intentionally the central bean-context configuration hub (also covers Collection.toArray() usage) 182 "rawtypes", // Raw types necessary for generic type handling 183 "unchecked" // Type erasure requires unchecked casts 184}) 185public class MarshallingContext extends Context implements ConversionFinder, BeanTypeResolver { 186 187 /** 188 * Builder class. 189 */ 190 public static class Builder extends Context.Builder<Builder> { 191 192 private static final Cache<HashKey,MarshallingContext> CACHE = Cache.of(HashKey.class, MarshallingContext.class).build(); 193 194 private static int integer(boolean...values) { 195 var n = 0; 196 for (var b : values) 197 n = (n << 1) | (b ? 1 : 0); 198 return n; 199 } 200 private Visibility beanClassVisibility; 201 private Visibility beanConstructorVisibility; 202 private Visibility beanMethodVisibility; 203 private Visibility beanFieldVisibility; 204 private boolean disableBeansRequireSomeProperties; 205 private boolean beanMapPutReturnsOldValue; 206 private boolean beansRequireDefaultConstructor; 207 private boolean beansRequireSerializable; 208 private boolean beansRequireSettersForGetters; 209 private boolean disableIgnoreTransientFields; 210 private boolean disableIgnoreUnknownNullBeanProperties; 211 private boolean disableIgnoreMissingSetters; 212 private boolean disableInterfaceProxies; 213 private boolean findFluentSetters; 214 private boolean ignoreInvocationExceptionsOnGetters; 215 private boolean ignoreInvocationExceptionsOnSetters; 216 private boolean ignoreUnknownBeanProperties; 217 private boolean ignoreUnknownEnumValues; 218 private boolean unsortedProperties; 219 private boolean recordComponentOrder; 220 private boolean useJavaBeanIntrospector; 221 private boolean validateSchema; 222 private String typePropertyName; 223 private MediaType mediaType; 224 private Locale locale; 225 private TimeZone timeZone; 226 private DurationFormat durationFormat; 227 private PeriodFormat periodFormat; 228 private CalendarFormat calendarFormat; 229 private DateFormat dateFormat; 230 private TemporalFormat temporalFormat; 231 private TimeZoneFormat timeZoneFormat; 232 private LocaleFormat localeFormat; 233 private BinaryFormat binaryFormat; 234 private EnumFormat enumFormat; 235 private UuidFormat uuidFormat; 236 private BitSetFormat bitSetFormat; 237 private BigNumberFormat bigNumberFormat; 238 private BooleanFormat booleanFormat; 239 private FloatFormat floatFormat; 240 private CurrencyFormat currencyFormat; 241 private ClassFormat classFormat; 242 private ClassLoader classLoader; 243 private Class<? extends PropertyNamer> propertyNamer; 244 private final List<ClassInfo> beanDictionary; 245 private final List<Object> swaps; 246 private final Set<ClassInfo> notBeanClasses; 247 private final Set<String> notBeanPackages; 248 private BeanStore beanStore; 249 private String activeView; 250 private boolean disableDefaultViewInclusion; 251 252 /** 253 * Constructor. 254 * 255 * All default settings. 256 */ 257 protected Builder() { 258 beanClassVisibility = env("MarshallingContext.beanClassVisibility", PUBLIC); 259 beanConstructorVisibility = env("MarshallingContext.beanConstructorVisibility", PUBLIC); 260 beanDictionary = list(); 261 beanFieldVisibility = env("MarshallingContext.beanFieldVisibility", PUBLIC); 262 beanMapPutReturnsOldValue = env("MarshallingContext.beanMapPutReturnsOldValue", false); 263 beanMethodVisibility = env("MarshallingContext.beanMethodVisibility", PUBLIC); 264 beansRequireDefaultConstructor = env("MarshallingContext.beansRequireDefaultConstructor", false); 265 beansRequireSerializable = env("MarshallingContext.beansRequireSerializable", false); 266 beansRequireSettersForGetters = env("MarshallingContext.beansRequireSettersForGetters", false); 267 disableBeansRequireSomeProperties = env("MarshallingContext.disableBeansRequireSomeProperties", false); 268 disableIgnoreMissingSetters = env("MarshallingContext.disableIgnoreMissingSetters", false); 269 disableIgnoreTransientFields = env("MarshallingContext.disableIgnoreTransientFields", false); 270 disableIgnoreUnknownNullBeanProperties = env("MarshallingContext.disableIgnoreUnknownNullBeanProperties", false); 271 disableInterfaceProxies = env("MarshallingContext.disableInterfaceProxies", false); 272 findFluentSetters = env("MarshallingContext.findFluentSetters", false); 273 ignoreInvocationExceptionsOnGetters = env("MarshallingContext.ignoreInvocationExceptionsOnGetters", false); 274 ignoreInvocationExceptionsOnSetters = env("MarshallingContext.ignoreInvocationExceptionsOnSetters", false); 275 ignoreUnknownBeanProperties = env("MarshallingContext.ignoreUnknownBeanProperties", false); 276 ignoreUnknownEnumValues = env("MarshallingContext.ignoreUnknownEnumValues", false); 277 locale = env("MarshallingContext.locale").map(Locale::forLanguageTag).orElse(Locale.getDefault()); 278 mediaType = env("MarshallingContext.mediaType").map(MediaType::of).orElse(null); 279 notBeanClasses = new TreeSet<>(); 280 notBeanPackages = new TreeSet<>(); 281 propertyNamer = null; 282 unsortedProperties = env("MarshallingContext.unsortedProperties", false); 283 recordComponentOrder = env("MarshallingContext.recordComponentOrder", false); 284 swaps = list(); 285 timeZone = env("MarshallingContext.timeZone").map(TimeZone::getTimeZone).orElse(null); 286 durationFormat = env("MarshallingContext.durationFormat", DurationFormat.ISO_8601_WITH_DAYS); 287 periodFormat = env("MarshallingContext.periodFormat", PeriodFormat.ISO_8601); 288 calendarFormat = env("MarshallingContext.calendarFormat", CalendarFormat.ISO_OFFSET_DATE_TIME); 289 dateFormat = env("MarshallingContext.dateFormat", DateFormat.ISO_LOCAL_DATE_TIME); 290 temporalFormat = env("MarshallingContext.temporalFormat", TemporalFormat.DEFAULT); 291 timeZoneFormat = env("MarshallingContext.timeZoneFormat", TimeZoneFormat.ID); 292 localeFormat = env("MarshallingContext.localeFormat", LocaleFormat.BCP_47); 293 binaryFormat = env("MarshallingContext.binaryFormat", BinaryFormat.NOT_SET); 294 enumFormat = env("MarshallingContext.enumFormat", EnumFormat.TO_STRING); 295 uuidFormat = env("MarshallingContext.uuidFormat", UuidFormat.STANDARD); 296 bitSetFormat = env("MarshallingContext.bitSetFormat", BitSetFormat.INDICES); 297 bigNumberFormat = env("MarshallingContext.bigNumberFormat", BigNumberFormat.NUMBER); 298 booleanFormat = env("MarshallingContext.booleanFormat", BooleanFormat.TRUE_FALSE); 299 floatFormat = env("MarshallingContext.floatFormat", FloatFormat.NaN_AS_NULL); 300 currencyFormat = env("MarshallingContext.currencyFormat", CurrencyFormat.ISO_CODE); 301 classFormat = env("MarshallingContext.classFormat", ClassFormat.FQCN); 302 typePropertyName = env("MarshallingContext.typePropertyName", "_type"); 303 useJavaBeanIntrospector = env("MarshallingContext.useJavaBeanIntrospector", false); 304 validateSchema = env("MarshallingContext.validateSchema", false); 305 beanStore = null; 306 } 307 308 /** 309 * Copy constructor. 310 * 311 * @param copyFrom The bean to copy from. 312 */ 313 protected Builder(MarshallingContext copyFrom) { 314 super(copyFrom); 315 beanClassVisibility = copyFrom.beanClassVisibility; 316 beanConstructorVisibility = copyFrom.beanConstructorVisibility; 317 beanDictionary = cp(copyFrom.beanDictionary); 318 beanFieldVisibility = copyFrom.beanFieldVisibility; 319 beanMapPutReturnsOldValue = copyFrom.beanMapPutReturnsOldValue; 320 beanMethodVisibility = copyFrom.beanMethodVisibility; 321 beansRequireDefaultConstructor = copyFrom.beansRequireDefaultConstructor; 322 beansRequireSerializable = copyFrom.beansRequireSerializable; 323 beansRequireSettersForGetters = copyFrom.beansRequireSettersForGetters; 324 disableBeansRequireSomeProperties = ! copyFrom.beansRequireSomeProperties; 325 disableIgnoreMissingSetters = ! copyFrom.ignoreMissingSetters; 326 disableIgnoreTransientFields = ! copyFrom.ignoreTransientFields; 327 disableIgnoreUnknownNullBeanProperties = ! copyFrom.ignoreUnknownNullBeanProperties; 328 disableInterfaceProxies = ! copyFrom.useInterfaceProxies; 329 findFluentSetters = copyFrom.findFluentSetters; 330 ignoreInvocationExceptionsOnGetters = copyFrom.ignoreInvocationExceptionsOnGetters; 331 ignoreInvocationExceptionsOnSetters = copyFrom.ignoreInvocationExceptionsOnSetters; 332 ignoreUnknownBeanProperties = copyFrom.ignoreUnknownBeanProperties; 333 ignoreUnknownEnumValues = copyFrom.ignoreUnknownEnumValues; 334 locale = copyFrom.locale; 335 mediaType = copyFrom.mediaType; 336 notBeanClasses = new TreeSet<>(copyFrom.notBeanClasses); 337 notBeanPackages = toSortedSet(copyFrom.notBeanPackages, false); 338 propertyNamer = copyFrom.propertyNamer; 339 unsortedProperties = copyFrom.unsortedProperties; 340 recordComponentOrder = copyFrom.recordComponentOrder; 341 swaps = cp(copyFrom.swaps); 342 timeZone = copyFrom.timeZone; 343 durationFormat = copyFrom.durationFormat; 344 periodFormat = copyFrom.periodFormat; 345 calendarFormat = copyFrom.calendarFormat; 346 dateFormat = copyFrom.dateFormat; 347 temporalFormat = copyFrom.temporalFormat; 348 timeZoneFormat = copyFrom.timeZoneFormat; 349 localeFormat = copyFrom.localeFormat; 350 binaryFormat = copyFrom.binaryFormat; 351 enumFormat = copyFrom.enumFormat; 352 uuidFormat = copyFrom.uuidFormat; 353 bitSetFormat = copyFrom.bitSetFormat; 354 bigNumberFormat = copyFrom.bigNumberFormat; 355 booleanFormat = copyFrom.booleanFormat; 356 floatFormat = copyFrom.floatFormat; 357 currencyFormat = copyFrom.currencyFormat; 358 classFormat = copyFrom.classFormat; 359 classLoader = copyFrom.classLoader; 360 typePropertyName = copyFrom.typePropertyName; 361 useJavaBeanIntrospector = copyFrom.useJavaBeanIntrospector; 362 validateSchema = copyFrom.validateSchema; 363 beanStore = copyFrom.beanStore; 364 activeView = copyFrom.activeView; 365 disableDefaultViewInclusion = ! copyFrom.defaultViewInclusion; 366 } 367 368 /** 369 * Copy constructor. 370 * 371 * @param copyFrom The builder to copy from. 372 */ 373 protected Builder(Builder copyFrom) { 374 super(copyFrom); 375 beanClassVisibility = copyFrom.beanClassVisibility; 376 beanConstructorVisibility = copyFrom.beanConstructorVisibility; 377 beanDictionary = cp(copyFrom.beanDictionary); 378 beanFieldVisibility = copyFrom.beanFieldVisibility; 379 beanMapPutReturnsOldValue = copyFrom.beanMapPutReturnsOldValue; 380 beanMethodVisibility = copyFrom.beanMethodVisibility; 381 beansRequireDefaultConstructor = copyFrom.beansRequireDefaultConstructor; 382 beansRequireSerializable = copyFrom.beansRequireSerializable; 383 beansRequireSettersForGetters = copyFrom.beansRequireSettersForGetters; 384 disableBeansRequireSomeProperties = copyFrom.disableBeansRequireSomeProperties; 385 disableIgnoreMissingSetters = copyFrom.disableIgnoreMissingSetters; 386 disableIgnoreTransientFields = copyFrom.disableIgnoreTransientFields; 387 disableIgnoreUnknownNullBeanProperties = copyFrom.disableIgnoreUnknownNullBeanProperties; 388 disableInterfaceProxies = copyFrom.disableInterfaceProxies; 389 findFluentSetters = copyFrom.findFluentSetters; 390 ignoreInvocationExceptionsOnGetters = copyFrom.ignoreInvocationExceptionsOnGetters; 391 ignoreInvocationExceptionsOnSetters = copyFrom.ignoreInvocationExceptionsOnSetters; 392 ignoreUnknownBeanProperties = copyFrom.ignoreUnknownBeanProperties; 393 ignoreUnknownEnumValues = copyFrom.ignoreUnknownEnumValues; 394 locale = copyFrom.locale; 395 mediaType = copyFrom.mediaType; 396 notBeanClasses = new TreeSet<>(copyFrom.notBeanClasses); 397 notBeanPackages = toSortedSet(copyFrom.notBeanPackages); 398 propertyNamer = copyFrom.propertyNamer; 399 unsortedProperties = copyFrom.unsortedProperties; 400 recordComponentOrder = copyFrom.recordComponentOrder; 401 swaps = cp(copyFrom.swaps); 402 timeZone = copyFrom.timeZone; 403 durationFormat = copyFrom.durationFormat; 404 periodFormat = copyFrom.periodFormat; 405 calendarFormat = copyFrom.calendarFormat; 406 dateFormat = copyFrom.dateFormat; 407 temporalFormat = copyFrom.temporalFormat; 408 timeZoneFormat = copyFrom.timeZoneFormat; 409 localeFormat = copyFrom.localeFormat; 410 binaryFormat = copyFrom.binaryFormat; 411 enumFormat = copyFrom.enumFormat; 412 uuidFormat = copyFrom.uuidFormat; 413 bitSetFormat = copyFrom.bitSetFormat; 414 bigNumberFormat = copyFrom.bigNumberFormat; 415 booleanFormat = copyFrom.booleanFormat; 416 floatFormat = copyFrom.floatFormat; 417 currencyFormat = copyFrom.currencyFormat; 418 classFormat = copyFrom.classFormat; 419 classLoader = copyFrom.classLoader; 420 typePropertyName = copyFrom.typePropertyName; 421 useJavaBeanIntrospector = copyFrom.useJavaBeanIntrospector; 422 validateSchema = copyFrom.validateSchema; 423 beanStore = copyFrom.beanStore; 424 activeView = copyFrom.activeView; 425 disableDefaultViewInclusion = copyFrom.disableDefaultViewInclusion; 426 } 427 428 /** 429 * Minimum bean class visibility. 430 * 431 * <p> 432 * Classes are not considered beans unless they meet the minimum visibility requirements. 433 * For example, if the visibility is <jsf>PUBLIC</jsf> and the bean class is <jk>protected</jk>, then the class 434 * will not be interpreted as a bean class and be serialized as a string. 435 * Use this setting to reduce the visibility requirement. 436 * 437 * <h5 class='section'>Example:</h5> 438 * <p class='bjava'> 439 * <jc>// A bean with a protected class and one field.</jc> 440 * <jk>protected class</jk> MyBean { 441 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 442 * } 443 * 444 * <jc>// Create a serializer that's capable of serializing the class.</jc> 445 * WriterSerializer <jv>serializer</jv> = JsonSerializer 446 * .<jsm>create</jsm>() 447 * .beanClassVisibility(<jsf>PROTECTED</jsf>) 448 * .build(); 449 * 450 * <jc>// Produces: {"foo","bar"}</jc> 451 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 452 * </p> 453 * 454 * <h5 class='section'>Notes:</h5><ul> 455 * <li class='note'>The {@link Bean @Marshalled} annotation can be used on a non-public bean class to override this setting. 456 * <li class='note'>The {@link BeanIgnore @BeanIgnore} annotation can also be used on a public bean class to ignore it as a bean. 457 * </ul> 458 * 459 * <h5 class='section'>See Also:</h5><ul> 460 * <li class='ja'>{@link BeanConfig#beanClassVisibility()} 461 * </ul> 462 * 463 * @param value 464 * The new value for this setting. 465 * <br>The default is {@link Visibility#PUBLIC}. 466 * <br>Cannot be <jk>null</jk>. 467 * @return This object. 468 */ 469 public Builder beanClassVisibility(Visibility value) { 470 beanClassVisibility = reqnn("value", value); 471 return this; 472 } 473 474 /** 475 * Minimum bean constructor visibility. 476 * 477 * <p> 478 * Only look for constructors with the specified minimum visibility. 479 * 480 * <p> 481 * This setting affects the logic for finding no-arg constructors for bean. Normally, only <jk>public</jk> no-arg 482 * constructors are used. Use this setting if you want to reduce the visibility requirement. 483 * 484 * <h5 class='section'>Example:</h5> 485 * <p class='bjava'> 486 * <jc>// A bean with a protected constructor and one field.</jc> 487 * <jk>public class</jk> MyBean { 488 * <jk>public</jk> String <jf>foo</jf>; 489 * 490 * <jk>protected</jk> MyBean() {} 491 * } 492 * 493 * <jc>// Create a parser capable of calling the protected constructor.</jc> 494 * ReaderParser <jv>parser</jv> = ReaderParser 495 * .<jsm>create</jsm>() 496 * .beanConstructorVisibility(<jsf>PROTECTED</jsf>) 497 * .build(); 498 * 499 * <jc>// Use it.</jc> 500 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{foo:'bar'}"</js>, MyBean.<jk>class</jk>); 501 * </p> 502 * 503 * <h5 class='section'>Notes:</h5><ul> 504 * <li class='note'>The {@link BeanCtor @BeanCtor} annotation can also be used to expose a non-public constructor. 505 * <li class='note'>The {@link BeanIgnore @BeanIgnore} annotation can also be used on a public bean constructor to ignore it. 506 * </ul> 507 * 508 * <h5 class='section'>See Also:</h5><ul> 509 * <li class='ja'>{@link BeanConfig#beanConstructorVisibility()} 510 * </ul> 511 * 512 * @param value 513 * The new value for this setting. 514 * <br>The default is {@link Visibility#PUBLIC}. 515 * <br>Cannot be <jk>null</jk>. 516 * @return This object. 517 */ 518 public Builder beanConstructorVisibility(Visibility value) { 519 beanConstructorVisibility = reqnn("value", value); 520 return this; 521 } 522 523 /** 524 * Returns the bean dictionary list. 525 * 526 * <p> 527 * Gives access to the inner list if you need to make more than simple additions via {@link #beanDictionary(Class...)}. 528 * 529 * @return The bean dictionary list. 530 * @see #beanDictionary(Class...) 531 */ 532 public List<ClassInfo> beanDictionary() { 533 return beanDictionary; 534 } 535 536 /** 537 * Convenience method for {@link #beanDictionary(ClassInfo...)} that accepts {@link Class} objects. 538 * 539 * @param values 540 * The values to add to this setting. 541 * <br>Cannot contain <jk>null</jk> values. 542 * @return This object. 543 * @see #beanDictionary(ClassInfo...) 544 */ 545 public Builder beanDictionary(Class<?>...values) { 546 reqnns("values", values); 547 return beanDictionary(Stream.of(values).map(x -> info(x)).toArray(ClassInfo[]::new)); 548 } 549 550 /** 551 * Bean dictionary. 552 * 553 * <p> 554 * The list of classes that make up the bean dictionary in this bean context. 555 * 556 * <p> 557 * Values are prepended to the list so that later calls can override classes of earlier calls. 558 * 559 * <p> 560 * A dictionary is a name/class mapping used to find class types during parsing when they cannot be inferred 561 * through reflection. The names are defined through the {@link Marshalled#typeName() @Marshalled(typeName)} annotation defined 562 * on the bean class. For example, if a class <c>Foo</c> has a type-name of <js>"myfoo"</js>, then it would end up 563 * serialized as <js>"{_type:'myfoo',...}"</js> in JSON 564 * or <js>"<myfoo>...</myfoo>"</js> in XML. 565 * 566 * <p> 567 * This setting tells the parsers which classes to look for when resolving <js>"_type"</js> attributes. 568 * 569 * <p> 570 * Values can consist of any of the following types: 571 * <ul> 572 * <li>Any bean class that specifies a value for {@link Marshalled#typeName() @Marshalled(typeName)}. 573 * <li>Any subclass of {@link BeanDictionaryList} containing a collection of bean classes with type name annotations. 574 * <li>Any subclass of {@link BeanDictionaryMap} containing a mapping of type names to classes without type name annotations. 575 * <li>Any array or collection of the objects above. 576 * </ul> 577 * 578 * <h5 class='section'>Example:</h5> 579 * <p class='bjava'> 580 * <jc>// POJOs with @Marshalled(name) annotations.</jc> 581 * <ja>@Marshalled</ja>(typeName=<js>"foo"</js>) 582 * <jk>public class</jk> Foo {...} 583 * <ja>@Marshalled</ja>(typeName=<js>"bar"</js>) 584 * <jk>public class</jk> Bar {...} 585 * 586 * <jc>// Create a parser and tell it which classes to try to resolve.</jc> 587 * ReaderParser <jv>parser</jv> = JsonParser 588 * .<jsm>create</jsm>() 589 * .dictionary(Foo.<jk>class</jk>, Bar.<jk>class</jk>) 590 * .addBeanTypes() 591 * .build(); 592 * 593 * <jc>// A bean with a field with an indeterminate type.</jc> 594 * <jk>public class</jk> MyBean { 595 * <jk>public</jk> Object <jf>mySimpleField</jf>; 596 * } 597 * 598 * <jc>// Parse bean.</jc> 599 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{mySimpleField:{_type:'foo',...}}"</js>, MyBean.<jk>class</jk>); 600 * </p> 601 * 602 * <p> 603 * Another option is to use the {@link Marshalled#dictionary()} annotation on the POJO class itself: 604 * 605 * <p class='bjava'> 606 * <jc>// Instead of by parser, define a bean dictionary on a class through an annotation.</jc> 607 * <jc>// This applies to all properties on this class and all subclasses.</jc> 608 * <ja>@Marshalled</ja>(dictionary={Foo.<jk>class</jk>,Bar.<jk>class</jk>}) 609 * <jk>public class</jk> MyBean { 610 * <jk>public</jk> Object <jf>mySimpleField</jf>; <jc>// May contain Foo or Bar object.</jc> 611 * <jk>public</jk> Map<String,Object> <jf>myMapField</jf>; <jc>// May contain Foo or Bar objects.</jc> 612 * } 613 * </p> 614 * 615 * <p> 616 * A typical usage is to allow for HTML documents to be parsed back into HTML beans: 617 * <p class='bjava'> 618 * <jc>// Use the predefined HTML5 bean dictionary which is a BeanDictionaryList.</jc> 619 * ReaderParser <jv>parser</jv> = HtmlParser 620 * .<jsm>create</jsm>() 621 * .dictionary(HtmlBeanDictionary.<jk>class</jk>) 622 * .build(); 623 * 624 * <jc>// Parse an HTML body into HTML beans.</jc> 625 * Body <jv>body</jv> = <jv>parser</jv>.read(<js>"<body><ul><li>foo</li><li>bar</li></ul>"</js>, Body.<jk>class</jk>); 626 * </p> 627 * 628 * <h5 class='section'>See Also:</h5><ul> 629 * <li class='ja'>{@link Marshalled#dictionary()} 630 * <li class='ja'>{@link MarshalledProp#dictionary()} 631 * <li class='ja'>{@link MarshalledConfig#dictionary()} 632 * <li class='ja'>{@link MarshalledConfig#dictionary_replace()} 633 * <li class='jm'>{@link MarshallingContext.Builder#beanDictionary(ClassInfo...)} 634 * </ul> 635 * 636 * @param values 637 * The values to add to this setting. 638 * <br>Cannot contain <jk>null</jk> values. 639 * @return This object. 640 */ 641 public Builder beanDictionary(ClassInfo...values) { 642 reqnns("values", values); 643 return beanDictionary(l(values)); 644 } 645 646 /** 647 * Same as {@link #beanDictionary(ClassInfo...)} but allows you to pass in a collection of class info objects. 648 * 649 * @param values 650 * The values to add to this setting. 651 * <br>Cannot be <jk>null</jk> or contain <jk>null</jk> values. 652 * @return This object. 653 * @see #beanDictionary(ClassInfo...) 654 */ 655 public Builder beanDictionary(Collection<ClassInfo> values) { 656 reqnns("values", values); 657 beanDictionary().addAll(0, values); 658 return this; 659 } 660 661 /** 662 * Minimum bean field visibility. 663 * 664 * <p> 665 * Only look for bean fields with the specified minimum visibility. 666 * 667 * <p> 668 * This affects which fields on a bean class are considered bean properties. Normally only <jk>public</jk> fields are considered. 669 * Use this setting if you want to reduce the visibility requirement. 670 * 671 * <h5 class='section'>Example:</h5> 672 * <p class='bjava'> 673 * <jc>// A bean with a protected field.</jc> 674 * <jk>public class</jk> MyBean { 675 * <jk>protected</jk> String <jf>foo</jf> = <js>"bar"</js>; 676 * } 677 * 678 * <jc>// Create a serializer that recognizes the protected field.</jc> 679 * WriterSerializer <jv>serializer</jv> = JsonSerializer 680 * .<jsm>create</jsm>() 681 * .beanFieldVisibility(<jsf>PROTECTED</jsf>) 682 * .build(); 683 * 684 * <jc>// Produces: {"foo":"bar"}</jc> 685 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 686 * </p> 687 * 688 * <p> 689 * Bean fields can be ignored as properties entirely by setting the value to {@link Visibility#NONE} 690 * 691 * <p class='bjava'> 692 * <jc>// Disable using fields as properties entirely.</jc> 693 * WriterSerializer <jv>serializer</jv> = JsonSerializer 694 * .<jsm>create</jsm>() 695 * .beanFieldVisibility(<jsf>NONE</jsf>) 696 * .build(); 697 * </p> 698 * 699 * <h5 class='section'>Notes:</h5><ul> 700 * <li class='note'>The {@link BeanProp @BeanProp} annotation can also be used to expose a non-public field. 701 * <li class='note'>The {@link BeanIgnore @BeanIgnore} annotation can also be used on a public bean field to ignore it as a bean property. 702 * </ul> 703 * 704 * <h5 class='section'>See Also:</h5><ul> 705 * <li class='ja'>{@link BeanConfig#beanFieldVisibility()} 706 * </ul> 707 * 708 * @param value 709 * The new value for this setting. 710 * <br>The default is {@link Visibility#PUBLIC}. 711 * <br>Cannot be <jk>null</jk>. 712 * @return This object. 713 */ 714 public Builder beanFieldVisibility(Visibility value) { 715 beanFieldVisibility = reqnn("value", value); 716 return this; 717 } 718 719 /** 720 * Bean interceptor. 721 * 722 * <p> 723 * Bean interceptors can be used to intercept calls to getters and setters and alter their values in transit. 724 * 725 * <h5 class='section'>Example:</h5> 726 * <p class='bjava'> 727 * <jc>// Interceptor that strips out sensitive information.</jc> 728 * <jk>public class</jk> AddressInterceptor <jk>extends</jk> BeanInterceptor<Address> { 729 * 730 * <jk>public</jk> Object readProperty(Address <jv>bean</jv>, String <jv>name</jv>, Object <jv>value</jv>) { 731 * <jk>if</jk> (<js>"taxInfo"</js>.equals(<jv>name</jv>)) 732 * <jk>return</jk> <js>"redacted"</js>; 733 * <jk>return</jk> <jv>value</jv>; 734 * } 735 * 736 * <jk>public</jk> Object writeProperty(Address <jv>bean</jv>, String <jv>name</jv>, Object <jv>value</jv>) { 737 * <jk>if</jk> (<js>"taxInfo"</js>.equals(<jv>name</jv>) && <js>"redacted"</js>.equals(<jv>value</jv>)) 738 * <jk>return</jk> TaxInfoUtils.<jsm>lookup</jsm>(<jv>bean</jv>.getStreet(), <jv>bean</jv>.getCity(), <jv>bean</jv>.getState()); 739 * <jk>return</jk> <jv>value</jv>; 740 * } 741 * } 742 * 743 * <jc>// Our bean class.</jc> 744 * <jk>public class</jk> Address { 745 * <jk>public</jk> String getTaxInfo() {...} 746 * <jk>public void</jk> setTaxInfo(String <jv>value</jv>) {...} 747 * } 748 * 749 * <jc>// Register filter on serializer or parser.</jc> 750 * WriterSerializer <jv>serializer</jv> = JsonSerializer 751 * .<jsm>create</jsm>() 752 * .beanInterceptor(Address.<jk>class</jk>, AddressInterceptor.<jk>class</jk>) 753 * .build(); 754 * 755 * <jc>// Produces: {"taxInfo":"redacted"}</jc> 756 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> Address()); 757 * </p> 758 * 759 * <h5 class='section'>See Also:</h5><ul> 760 * <li class='jc'>{@link BeanInterceptor} 761 * <li class='ja'>{@link Marshalled#interceptor() @Marshalled(interceptor)} 762 * </ul> 763 * 764 * @param on The bean that the filter applies to. 765 * <br>Cannot be <jk>null</jk>. 766 * @param value 767 * The new value for this setting. 768 * <br>Cannot be <jk>null</jk>. 769 * @return This object. 770 */ 771 public Builder beanInterceptor(Class<?> on, Class<? extends BeanInterceptor<?>> value) { 772 reqnn("on", on); 773 reqnn("value", value); 774 return annotations(MarshalledApplyAnnotation.create(on).value(MarshalledAnnotation.create().interceptor(value).build()).build()); 775 } 776 777 /** 778 * BeanMap.put() returns old property value. 779 * 780 * <p> 781 * When enabled, then the {@link BeanMap#put(String,Object) BeanMap.put()} method will return old property 782 * values. Otherwise, it returns <jk>null</jk>. 783 * 784 * <p> 785 * Disabled by default because it introduces a slight performance penalty during serialization. 786 * 787 * <h5 class='section'>Example:</h5> 788 * <p class='bjava'> 789 * <jc>// Create a context that creates BeanMaps with normal put() behavior.</jc> 790 * MarshallingContext <jv>context</jv> = MarshallingContext 791 * .<jsm>create</jsm>() 792 * .beanMapPutReturnsOldValue() 793 * .build(); 794 * 795 * BeanMap<MyBean> <jv>myBeanMap</jv> = <jv>context</jv>.createSession().toBeanMap(<jk>new</jk> MyBean()); 796 * <jv>myBeanMap</jv>.put(<js>"foo"</js>, <js>"bar"</js>); 797 * Object <jv>oldValue</jv> = <jv>myBeanMap</jv>.put(<js>"foo"</js>, <js>"baz"</js>); <jc>// oldValue == "bar"</jc> 798 * </p> 799 * 800 * <h5 class='section'>See Also:</h5><ul> 801 * <li class='ja'>{@link BeanConfig#beanMapPutReturnsOldValue()} 802 * </ul> 803 * 804 * @return This object. 805 */ 806 public Builder beanMapPutReturnsOldValue() { 807 return beanMapPutReturnsOldValue(true); 808 } 809 810 /** 811 * Same as {@link #beanMapPutReturnsOldValue()} but allows you to explicitly specify the value. 812 * 813 * @param value The value for this setting. 814 * @return This object. 815 */ 816 public Builder beanMapPutReturnsOldValue(boolean value) { 817 beanMapPutReturnsOldValue = value; 818 return this; 819 } 820 821 /** 822 * Minimum bean method visibility. 823 * 824 * <p> 825 * Only look for bean methods with the specified minimum visibility. 826 * 827 * <p> 828 * This affects which methods are detected as getters and setters on a bean class. Normally only <jk>public</jk> getters and setters are considered. 829 * Use this setting if you want to reduce the visibility requirement. 830 * 831 * <h5 class='section'>Example:</h5> 832 * <p class='bjava'> 833 * <jc>// A bean with a protected getter.</jc> 834 * <jk>public class</jk> MyBean { 835 * <jk>public</jk> String getFoo() { <jk>return</jk> <js>"foo"</js>; } 836 * <jk>protected</jk> String getBar() { <jk>return</jk> <js>"bar"</js>; } 837 * } 838 * 839 * <jc>// Create a serializer that looks for protected getters and setters.</jc> 840 * WriterSerializer <jv>serializer</jv> = JsonSerializer 841 * .<jsm>create</jsm>() 842 * .beanMethodVisibility(<jsf>PROTECTED</jsf>) 843 * .build(); 844 * 845 * <jc>// Produces: {"foo":"foo","bar":"bar"}</jc> 846 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 847 * </p> 848 * 849 * <h5 class='section'>Notes:</h5><ul> 850 * <li class='note'>The {@link BeanProp @BeanProp} annotation can also be used to expose a non-public method. 851 * <li class='note'>The {@link BeanIgnore @BeanIgnore} annotation can also be used on a public bean getter/setter to ignore it as a bean property. 852 * </ul> 853 * 854 * <h5 class='section'>See Also:</h5><ul> 855 * <li class='ja'>{@link BeanConfig#beanMethodVisibility()} 856 * </ul> 857 * 858 * @param value 859 * The new value for this setting. 860 * <br>The default is {@link Visibility#PUBLIC} 861 * @return This object. 862 */ 863 public Builder beanMethodVisibility(Visibility value) { 864 beanMethodVisibility = reqnn("value", value); 865 return this; 866 } 867 868 /** 869 * Bean property includes. 870 * 871 * <p> 872 * Specifies the set and order of names of properties associated with the bean class. 873 * 874 * <p> 875 * For example, <c>beanProperties(MyBean.<jk>class</jk>, <js>"foo,bar"</js>)</c> means only serialize the <c>foo</c> and 876 * <c>bar</c> properties on the specified bean. Likewise, parsing will ignore any bean properties not specified 877 * and either throw an exception or silently ignore them depending on whether {@link #ignoreUnknownBeanProperties()} 878 * has been called. 879 * 880 * <p> 881 * This value is entirely optional if you simply want to expose all the getters and public fields on 882 * a class as bean properties. However, it's useful if you want certain getters to be ignored or you want the properties to be 883 * serialized in a particular order. Note that on IBM JREs, the property order is the same as the order in the source code, 884 * whereas on Oracle JREs, the order is entirely random. 885 * 886 * <p> 887 * Setting applies to specified class and all subclasses. 888 * 889 * <h5 class='section'>Example:</h5> 890 * <p class='bjava'> 891 * <jc>// A bean with 3 properties.</jc> 892 * <jk>public class</jk> MyBean { 893 * <jk>public</jk> String 894 * <jf>foo</jf> = <js>"foo"</js>, 895 * <jf>bar</jf> = <js>"bar"</js>, 896 * <jf>baz</jf> = <js>"baz"</js>; 897 * } 898 * 899 * <jc>// Create a serializer that includes only the 'foo' and 'bar' properties on the MyBean class.</jc> 900 * WriterSerializer <jv>serializer</jv> = JsonSerializer 901 * .<jsm>create</jsm>() 902 * .beanProperties(MyBean.<jk>class</jk>, <js>"foo,bar"</js>) 903 * .build(); 904 * 905 * <jc>// Produces: {"foo":"foo","bar":"bar"}</jc> 906 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 907 * </p> 908 * 909 * <p> 910 * This method is functionally equivalent to the following code: 911 * <p class='bjava'> 912 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>beanClass</jv>).properties(<jv>properties</jv>).build()); 913 * </p> 914 * 915 * <h5 class='section'>See Also:</h5><ul> 916 * <li class='jma'>{@link BeanType#properties()}/{@link BeanType#p()} - On an annotation on the bean class itself. 917 * </ul> 918 * 919 * @param beanClass The bean class. 920 * <br>Cannot be <jk>null</jk>. 921 * @param properties Comma-delimited list of property names. 922 * <br>Cannot be <jk>null</jk>. 923 * @return This object. 924 */ 925 public Builder beanProperties(Class<?> beanClass, String properties) { 926 reqnn("beanClass", beanClass); 927 reqnn("properties", properties); 928 return annotations(BeanTypeApplyAnnotation.create(beanClass).value(BeanTypeAnnotation.create().p(properties).build()).build()); 929 } 930 931 /** 932 * Bean property includes. 933 * 934 * <p> 935 * Specifies the set and order of names of properties associated with bean classes. 936 * 937 * <p> 938 * For example, <c>beanProperties(AMap.<jsm>of</jsm>(<js>"MyBean"</js>, <js>"foo,bar"</js>))</c> means only serialize the <c>foo</c> and 939 * <c>bar</c> properties on the specified bean. Likewise, parsing will ignore any bean properties not specified 940 * and either throw an exception or silently ignore them depending on whether {@link #ignoreUnknownBeanProperties()} 941 * has been called. 942 * 943 * <p> 944 * This value is entirely optional if you simply want to expose all the getters and public fields on 945 * a class as bean properties. However, it's useful if you want certain getters to be ignored or you want the properties to be 946 * serialized in a particular order. Note that on IBM JREs, the property order is the same as the order in the source code, 947 * whereas on Oracle JREs, the order is entirely random. 948 * 949 * <p> 950 * Setting applies to specified class and all subclasses. 951 * 952 * <h5 class='section'>Example:</h5> 953 * <p class='bjava'> 954 * <jc>// A bean with 3 properties.</jc> 955 * <jk>public class</jk> MyBean { 956 * <jk>public</jk> String 957 * <jf>foo</jf> = <js>"foo"</js>, 958 * <jf>bar</jf> = <js>"bar"</js>, 959 * <jf>baz</jf> = <js>"baz"</js>; 960 * } 961 * 962 * <jc>// Create a serializer that includes only the 'foo' and 'bar' properties on the MyBean class.</jc> 963 * WriterSerializer <jv>serializer</jv> = JsonSerializer 964 * .<jsm>create</jsm>() 965 * .beanProperties(AMap.<jsm>of</jsm>(<js>"MyBean"</js>, <js>"foo,bar"</js>)) 966 * .build(); 967 * 968 * <jc>// Produces: {"foo":"foo","bar":"bar"}</jc> 969 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 970 * </p> 971 * 972 * <p> 973 * This method is functionally equivalent to the following code for each entry: 974 * <p class='bjava'> 975 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>key</jv>).properties(<jv>value</jv>.toString()).build()); 976 * </p> 977 * 978 * <h5 class='section'>See Also:</h5><ul> 979 * <li class='jma'>{@link BeanType#properties()} / {@link BeanType#p()}- On an annotation on the bean class itself. 980 * </ul> 981 * 982 * @param values 983 * The values to add to this builder. 984 * <br>Keys are bean class names which can be a simple name, fully-qualified name, or <js>"*"</js> for all beans. 985 * <br>Values are comma-delimited lists of property names. Non-String objects are first converted to Strings. 986 * <br>Cannot be <jk>null</jk>. 987 * @return This object. 988 */ 989 public Builder beanProperties(Map<String,Object> values) { 990 reqnn("values", values); 991 values.forEach((k, v) -> annotations(BeanTypeApplyAnnotation.create(k).value(BeanTypeAnnotation.create().p(s(v)).build()).build())); 992 return this; 993 } 994 995 /** 996 * Bean property includes. 997 * 998 * <p> 999 * Specifies the set and order of names of properties associated with the bean class. 1000 * 1001 * <p> 1002 * For example, <c>beanProperties(<js>"MyBean"</js>, <js>"foo,bar"</js>)</c> means only serialize the <c>foo</c> and 1003 * <c>bar</c> properties on the specified bean. Likewise, parsing will ignore any bean properties not specified 1004 * and either throw an exception or silently ignore them depending on whether {@link #ignoreUnknownBeanProperties()} 1005 * has been called. 1006 * 1007 * <p> 1008 * This value is entirely optional if you simply want to expose all the getters and public fields on 1009 * a class as bean properties. However, it's useful if you want certain getters to be ignored or you want the properties to be 1010 * serialized in a particular order. Note that on IBM JREs, the property order is the same as the order in the source code, 1011 * whereas on Oracle JREs, the order is entirely random. 1012 * 1013 * <p> 1014 * Setting applies to specified class and all subclasses. 1015 * 1016 * <h5 class='section'>Example:</h5> 1017 * <p class='bjava'> 1018 * <jc>// A bean with 3 properties.</jc> 1019 * <jk>public class</jk> MyBean { 1020 * <jk>public</jk> String 1021 * <jf>foo</jf> = <js>"foo"</js>, 1022 * <jf>bar</jf> = <js>"bar"</js>, 1023 * <jf>baz</jf> = <js>"baz"</js>; 1024 * } 1025 * 1026 * <jc>// Create a serializer that includes only the 'foo' and 'bar' properties on the MyBean class.</jc> 1027 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1028 * .<jsm>create</jsm>() 1029 * .beanProperties(<js>"MyBean"</js>, <js>"foo,bar"</js>) 1030 * .build(); 1031 * 1032 * <jc>// Produces: {"foo":"foo","bar":"bar"}</jc> 1033 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1034 * </p> 1035 * 1036 * <p> 1037 * This method is functionally equivalent to the following code: 1038 * <p class='bjava'> 1039 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>beanClassName</jv>).properties(<jv>properties</jv>).build()); 1040 * </p> 1041 * 1042 * <h5 class='section'>See Also:</h5><ul> 1043 * <li class='jma'>{@link BeanType#properties()} / {@link BeanType#p()} - On an annotation on the bean class itself. 1044 * </ul> 1045 * 1046 * @param beanClassName 1047 * The bean class name. 1048 * <br>Can be a simple name, fully-qualified name, or <js>"*"</js> for all beans. 1049 * @param properties Comma-delimited list of property names. 1050 * <br>Cannot be <jk>null</jk>. 1051 * @return This object. 1052 */ 1053 public Builder beanProperties(String beanClassName, String properties) { 1054 reqnn("beanClassName", beanClassName); 1055 reqnn("properties", properties); 1056 return annotations(BeanTypeApplyAnnotation.create(beanClassName).value(BeanTypeAnnotation.create().p(properties).build()).build()); 1057 } 1058 1059 /** 1060 * Bean property excludes. 1061 * 1062 * <p> 1063 * Specifies to exclude the specified list of properties for the specified bean class. 1064 * 1065 * <p> 1066 * Same as {@link #beanProperties(Class, String)} except you specify a list of bean property names that you want to exclude from 1067 * serialization. 1068 * 1069 * <p> 1070 * Setting applies to specified class and all subclasses. 1071 * 1072 * <h5 class='section'>Example:</h5> 1073 * <p class='bjava'> 1074 * <jc>// A bean with 3 properties.</jc> 1075 * <jk>public class</jk> MyBean { 1076 * <jk>public</jk> String 1077 * <jf>foo</jf> = <js>"foo"</js>, 1078 * <jf>bar</jf> = <js>"bar"</js>, 1079 * <jf>baz</jf> = <js>"baz"</js>; 1080 * } 1081 * 1082 * <jc>// Create a serializer that excludes the "bar" and "baz" properties on the MyBean class.</jc> 1083 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1084 * .<jsm>create</jsm>() 1085 * .beanPropertiesExcludes(MyBean.<jk>class</jk>, <js>"bar,baz"</js>) 1086 * .build(); 1087 * 1088 * <jc>// Produces: {"foo":"foo"}</jc> 1089 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1090 * </p> 1091 * 1092 * <p> 1093 * This method is functionally equivalent to the following code: 1094 * <p class='bjava'> 1095 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>beanClass</jv>).excludeProperties(<jv>properties</jv>).build()); 1096 * </p> 1097 * 1098 * <h5 class='section'>See Also:</h5><ul> 1099 * <li class='jma'>{@link BeanType#excludeProperties()} / {@link BeanType#xp()} 1100 * </ul> 1101 * 1102 * @param beanClass The bean class. 1103 * <br>Cannot be <jk>null</jk>. 1104 * @param properties Comma-delimited list of property names. 1105 * <br>Cannot be <jk>null</jk>. 1106 * @return This object. 1107 */ 1108 public Builder beanPropertiesExcludes(Class<?> beanClass, String properties) { 1109 reqnn("beanClass", beanClass); 1110 reqnn("properties", properties); 1111 return annotations(BeanTypeApplyAnnotation.create(beanClass).value(BeanTypeAnnotation.create().xp(properties).build()).build()); 1112 } 1113 1114 /** 1115 * Bean property excludes. 1116 * 1117 * <p> 1118 * Specifies to exclude the specified list of properties for the specified bean classes. 1119 * 1120 * <p> 1121 * Same as {@link #beanProperties(Map)} except you specify a list of bean property names that you want to exclude from 1122 * serialization. 1123 * 1124 * <p> 1125 * Setting applies to specified class and all subclasses. 1126 * 1127 * <h5 class='section'>Example:</h5> 1128 * <p class='bjava'> 1129 * <jc>// A bean with 3 properties.</jc> 1130 * <jk>public class</jk> MyBean { 1131 * <jk>public</jk> String 1132 * <jf>foo</jf> = <js>"foo"</js>, 1133 * <jf>bar</jf> = <js>"bar"</js>, 1134 * <jf>baz</jf> = <js>"baz"</js>; 1135 * } 1136 * 1137 * <jc>// Create a serializer that excludes the "bar" and "baz" properties on the MyBean class.</jc> 1138 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1139 * .<jsm>create</jsm>() 1140 * .beanPropertiesExcludes(AMap.of(<js>"MyBean"</js>, <js>"bar,baz"</js>)) 1141 * .build(); 1142 * 1143 * <jc>// Produces: {"foo":"foo"}</jc> 1144 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1145 * </p> 1146 * 1147 * <p> 1148 * This method is functionally equivalent to the following code for each entry: 1149 * <p class='bjava'> 1150 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>key</jv>).excludeProperties(<jv>value</jv>.toString()).build()); 1151 * </p> 1152 * 1153 * <h5 class='section'>See Also:</h5><ul> 1154 * <li class='jma'>{@link BeanType#excludeProperties()} / {@link BeanType#xp()} 1155 * </ul> 1156 * 1157 * @param values 1158 * The values to add to this builder. 1159 * <br>Keys are bean class names which can be a simple name, fully-qualified name, or <js>"*"</js> for all beans. 1160 * <br>Values are comma-delimited lists of property names. Non-String objects are first converted to Strings. 1161 * <br>Cannot be <jk>null</jk>. 1162 * @return This object. 1163 */ 1164 public Builder beanPropertiesExcludes(Map<String,Object> values) { 1165 reqnn("values", values); 1166 values.forEach((k, v) -> annotations(BeanTypeApplyAnnotation.create(k).value(BeanTypeAnnotation.create().xp(s(v)).build()).build())); 1167 return this; 1168 } 1169 1170 /** 1171 * Bean property excludes. 1172 * 1173 * <p> 1174 * Specifies to exclude the specified list of properties for the specified bean class. 1175 * 1176 * <p> 1177 * Same as {@link #beanPropertiesExcludes(String, String)} except you specify a list of bean property names that you want to exclude from 1178 * serialization. 1179 * 1180 * <p> 1181 * Setting applies to specified class and all subclasses. 1182 * 1183 * <h5 class='section'>Example:</h5> 1184 * <p class='bjava'> 1185 * <jc>// A bean with 3 properties.</jc> 1186 * <jk>public class</jk> MyBean { 1187 * <jk>public</jk> String 1188 * <jf>foo</jf> = <js>"foo"</js>, 1189 * <jf>bar</jf> = <js>"bar"</js>, 1190 * <jf>baz</jf> = <js>"baz"</js>; 1191 * } 1192 * 1193 * <jc>// Create a serializer that excludes the "bar" and "baz" properties on the MyBean class.</jc> 1194 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1195 * .<jsm>create</jsm>() 1196 * .beanPropertiesExcludes(<js>"MyBean"</js>, <js>"bar,baz"</js>) 1197 * .build(); 1198 * 1199 * <jc>// Produces: {"foo":"foo"}</jc> 1200 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1201 * </p> 1202 * 1203 * <p> 1204 * This method is functionally equivalent to the following code: 1205 * <p class='bjava'> 1206 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>beanClassName</jv>).excludeProperties(<jv>properties</jv>).build()); 1207 * </p> 1208 * 1209 * <h5 class='section'>See Also:</h5><ul> 1210 * <li class='jma'>{@link BeanType#excludeProperties()} / {@link BeanType#xp()} 1211 * </ul> 1212 * 1213 * @param beanClassName 1214 * The bean class name. 1215 * <br>Can be a simple name, fully-qualified name, or <js>"*"</js> for all bean classes. 1216 * <br>Cannot be <jk>null</jk>. 1217 * @param properties Comma-delimited list of property names. 1218 * <br>Cannot be <jk>null</jk>. 1219 * @return This object. 1220 */ 1221 public Builder beanPropertiesExcludes(String beanClassName, String properties) { 1222 reqnn("beanClassName", beanClassName); 1223 reqnn("properties", properties); 1224 return annotations(BeanTypeApplyAnnotation.create(beanClassName).value(BeanTypeAnnotation.create().xp(properties).build()).build()); 1225 } 1226 1227 /** 1228 * Read-only bean properties. 1229 * 1230 * <p> 1231 * Specifies one or more properties on a bean that are read-only despite having valid getters. 1232 * Serializers will serialize such properties as usual, but parsers will silently ignore them. 1233 * Note that this is different from the {@link #beanProperties(Class,String) beanProperties}/{@link #beanPropertiesExcludes(Class,String) beanPropertiesExcludes} settings which include or exclude properties 1234 * for both serializers and parsers. 1235 * 1236 * <h5 class='section'>Example:</h5> 1237 * <p class='bjava'> 1238 * <jc>// A bean with 3 properties.</jc> 1239 * <jk>public class</jk> MyBean { 1240 * <jk>public</jk> String <jf>foo</jf>, <jf>bar</jf>, <jf>baz</jf>; 1241 * } 1242 * 1243 * <jc>// Create a serializer with read-only property settings.</jc> 1244 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1245 * .<jsm>create</jsm>() 1246 * .beanPropertiesReadOnly(MyBean.<jk>class</jk>, <js>"bar,baz"</js>) 1247 * .build(); 1248 * 1249 * <jc>// All 3 properties will be serialized.</jc> 1250 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1251 * 1252 * <jc>// Create a parser with read-only property settings.</jc> 1253 * ReaderParser <jv>parser</jv> = JsonParser 1254 * .<jsm>create</jsm>() 1255 * .beanPropertiesReadOnly(MyBean.<jk>class</jk>, <js>"bar,baz"</js>) 1256 * .ignoreUnknownBeanProperties() 1257 * .build(); 1258 * 1259 * <jc>// Parser ignores bar and baz properties.</jc> 1260 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{foo:'foo',bar:'bar',baz:'baz'}"</js>, MyBean.<jk>class</jk>); 1261 * </p> 1262 * 1263 * <p> 1264 * This method is functionally equivalent to the following code: 1265 * <p class='bjava'> 1266 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>beanClass</jv>).readOnlyProperties(<jv>properties</jv>).build()); 1267 * </p> 1268 * 1269 * <h5 class='section'>See Also:</h5><ul> 1270 * <li class='jma'>{@link BeanType#readOnlyProperties()} / {@link BeanType#ro()} 1271 * </ul> 1272 * 1273 * @param beanClass The bean class. 1274 * <br>Cannot be <jk>null</jk>. 1275 * @param properties Comma-delimited list of property names. 1276 * <br>Cannot be <jk>null</jk>. 1277 * @return This object. 1278 */ 1279 public Builder beanPropertiesReadOnly(Class<?> beanClass, String properties) { 1280 reqnn("beanClass", beanClass); 1281 reqnn("properties", properties); 1282 return annotations(BeanTypeApplyAnnotation.create(beanClass).value(BeanTypeAnnotation.create().ro(properties).build()).build()); 1283 } 1284 1285 /** 1286 * Read-only bean properties. 1287 * 1288 * <p> 1289 * Specifies one or more properties on beans that are read-only despite having valid getters. 1290 * Serializers will serialize such properties as usual, but parsers will silently ignore them. 1291 * Note that this is different from the {@link #beanProperties(Class,String) beanProperties}/{@link #beanPropertiesExcludes(Class,String) beanPropertiesExcludes} settings which include or exclude properties 1292 * for both serializers and parsers. 1293 * 1294 * <h5 class='section'>Example:</h5> 1295 * <p class='bjava'> 1296 * <jc>// A bean with 3 properties.</jc> 1297 * <jk>public class</jk> MyBean { 1298 * <jk>public</jk> String <jf>foo</jf>, <jf>bar</jf>, <jf>baz</jf>; 1299 * } 1300 * 1301 * <jc>// Create a serializer with read-only property settings.</jc> 1302 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1303 * .<jsm>create</jsm>() 1304 * .beanPropertiesReadOnly(AMap.<jsm>of</jsm>(<js>"MyBean"</js>, <js>"bar,baz"</js>)) 1305 * .build(); 1306 * 1307 * <jc>// All 3 properties will be serialized.</jc> 1308 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1309 * 1310 * <jc>// Create a parser with read-only property settings.</jc> 1311 * ReaderParser <jv>parser</jv> = JsonParser 1312 * .<jsm>create</jsm>() 1313 * .beanPropertiesReadOnly(AMap.<jsm>of</jsm>(<js>"MyBean"</js>, <js>"bar,baz"</js>)) 1314 * .ignoreUnknownBeanProperties() 1315 * .build(); 1316 * 1317 * <jc>// Parser ignores bar and baz properties.</jc> 1318 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{foo:'foo',bar:'bar',baz:'baz'}"</js>, MyBean.<jk>class</jk>); 1319 * </p> 1320 * 1321 * <p> 1322 * This method is functionally equivalent to the following code for each entry: 1323 * <p class='bjava'> 1324 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>key</jv>).readOnlyProperties(<jv>value</jv>.toString()).build()); 1325 * </p> 1326 * 1327 * <h5 class='section'>See Also:</h5><ul> 1328 * <li class='jma'>{@link BeanType#readOnlyProperties()} / {@link BeanType#ro()} 1329 * </ul> 1330 * 1331 * @param values 1332 * The values to add to this builder. 1333 * <br>Keys are bean class names which can be a simple name, fully-qualified name, or <js>"*"</js> for all beans. 1334 * <br>Values are comma-delimited lists of property names. Non-String objects are first converted to Strings. 1335 * <br>Cannot be <jk>null</jk>. 1336 * @return This object. 1337 */ 1338 public Builder beanPropertiesReadOnly(Map<String,Object> values) { 1339 reqnn("values", values); 1340 values.forEach((k, v) -> annotations(BeanTypeApplyAnnotation.create(k).value(BeanTypeAnnotation.create().ro(s(v)).build()).build())); 1341 return this; 1342 } 1343 1344 /** 1345 * Read-only bean properties. 1346 * 1347 * <p> 1348 * Specifies one or more properties on a bean that are read-only despite having valid getters. 1349 * Serializers will serialize such properties as usual, but parsers will silently ignore them. 1350 * Note that this is different from the {@link #beanProperties(Class,String) beanProperties}/{@link #beanPropertiesExcludes(Class,String) beanPropertiesExcludes} settings which include or exclude properties 1351 * for both serializers and parsers. 1352 * 1353 * <h5 class='section'>Example:</h5> 1354 * <p class='bjava'> 1355 * <jc>// A bean with 3 properties.</jc> 1356 * <jk>public class</jk> MyBean { 1357 * <jk>public</jk> String <jf>foo</jf>, <jf>bar</jf>, <jf>baz</jf>; 1358 * } 1359 * 1360 * <jc>// Create a serializer with read-only property settings.</jc> 1361 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1362 * .<jsm>create</jsm>() 1363 * .beanPropertiesReadOnly(<js>"MyBean"</js>, <js>"bar,baz"</js>) 1364 * .build(); 1365 * 1366 * <jc>// All 3 properties will be serialized.</jc> 1367 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1368 * 1369 * <jc>// Create a parser with read-only property settings.</jc> 1370 * ReaderParser <jv>parser</jv> = JsonParser 1371 * .<jsm>create</jsm>() 1372 * .beanPropertiesReadOnly(<js>"MyBean"</js>, <js>"bar,baz"</js>) 1373 * .ignoreUnknownBeanProperties() 1374 * .build(); 1375 * 1376 * <jc>// Parser ignores bar and baz properties.</jc> 1377 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{foo:'foo',bar:'bar',baz:'baz'}"</js>, MyBean.<jk>class</jk>); 1378 * </p> 1379 * 1380 * <p> 1381 * This method is functionally equivalent to the following code: 1382 * <p class='bjava'> 1383 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>beanClassName</jv>).readOnlyProperties(<jv>properties</jv>).build()); 1384 * </p> 1385 * 1386 * <h5 class='section'>See Also:</h5><ul> 1387 * <li class='jma'>{@link BeanType#readOnlyProperties()} / {@link BeanType#ro()} 1388 * </ul> 1389 * 1390 * @param beanClassName 1391 * The bean class name. 1392 * <br>Can be a simple name, fully-qualified name, or <js>"*"</js> for all bean classes. 1393 * @param properties Comma-delimited list of property names. 1394 * @return This object. 1395 */ 1396 public Builder beanPropertiesReadOnly(String beanClassName, String properties) { 1397 reqnn("beanClassName", beanClassName); 1398 reqnn("properties", properties); 1399 return annotations(BeanTypeApplyAnnotation.create(beanClassName).value(BeanTypeAnnotation.create().ro(properties).build()).build()); 1400 } 1401 1402 /** 1403 * Write-only bean properties. 1404 * 1405 * <p> 1406 * Specifies one or more properties on a bean that are write-only despite having valid setters. 1407 * Parsers will parse such properties as usual, but serializers will silently ignore them. 1408 * Note that this is different from the {@link #beanProperties(Class,String) beanProperties}/{@link #beanPropertiesExcludes(Class,String) beanPropertiesExcludes} settings which include or exclude properties 1409 * for both serializers and parsers. 1410 * 1411 * <h5 class='section'>Example:</h5> 1412 * <p class='bjava'> 1413 * <jc>// A bean with 3 properties.</jc> 1414 * <jk>public class</jk> MyBean { 1415 * <jk>public</jk> String <jf>foo</jf>, <jf>bar</jf>, <jf>baz</jf>; 1416 * } 1417 * 1418 * <jc>// Create a serializer with write-only property settings.</jc> 1419 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1420 * .<jsm>create</jsm>() 1421 * .beanPropertiesWriteOnly(MyBean.<jk>class</jk>, <js>"bar,baz"</js>) 1422 * .build(); 1423 * 1424 * <jc>// Only foo will be serialized.</jc> 1425 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1426 * 1427 * <jc>// Create a parser with write-only property settings.</jc> 1428 * ReaderParser <jv>parser</jv> = JsonParser 1429 * .<jsm>create</jsm>() 1430 * .beanPropertiesWriteOnly(MyBean.<jk>class</jk>, <js>"bar,baz"</js>) 1431 * .build(); 1432 * 1433 * <jc>// Parser parses all 3 properties.</jc> 1434 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{foo:'foo',bar:'bar',baz:'baz'}"</js>, MyBean.<jk>class</jk>); 1435 * </p> 1436 * 1437 * <p> 1438 * This method is functionally equivalent to the following code: 1439 * <p class='bjava'> 1440 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>beanClass</jv>).writeOnlyProperties(<jv>properties</jv>).build()); 1441 * </p> 1442 * 1443 * <h5 class='section'>See Also:</h5><ul> 1444 * <li class='jma'>{@link BeanType#writeOnlyProperties()} / {@link BeanType#wo()} 1445 * </ul> 1446 * 1447 * @param beanClass The bean class. 1448 * <br>Cannot be <jk>null</jk>. 1449 * @param properties Comma-delimited list of property names. 1450 * <br>Cannot be <jk>null</jk>. 1451 * @return This object. 1452 */ 1453 public Builder beanPropertiesWriteOnly(Class<?> beanClass, String properties) { 1454 reqnn("beanClass", beanClass); 1455 reqnn("properties", properties); 1456 return annotations(BeanTypeApplyAnnotation.create(beanClass).value(BeanTypeAnnotation.create().wo(properties).build()).build()); 1457 } 1458 1459 /** 1460 * Write-only bean properties. 1461 * 1462 * <p> 1463 * Specifies one or more properties on a bean that are write-only despite having valid setters. 1464 * Parsers will parse such properties as usual, but serializers will silently ignore them. 1465 * Note that this is different from the {@link #beanProperties(Class,String) beanProperties}/{@link #beanPropertiesExcludes(Class,String) beanPropertiesExcludes} settings which include or exclude properties 1466 * for both serializers and parsers. 1467 * 1468 * <h5 class='section'>Example:</h5> 1469 * <p class='bjava'> 1470 * <jc>// A bean with 3 properties.</jc> 1471 * <jk>public class</jk> MyBean { 1472 * <jk>public</jk> String <jf>foo</jf>, <jf>bar</jf>, <jf>baz</jf>; 1473 * } 1474 * 1475 * <jc>// Create a serializer with write-only property settings.</jc> 1476 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1477 * .<jsm>create</jsm>() 1478 * .beanPropertiesWriteOnly(AMap.<jsm>of</jsm>(<js>"MyBean"</js>, <js>"bar,baz"</js>)) 1479 * .build(); 1480 * 1481 * <jc>// Only foo will be serialized.</jc> 1482 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1483 * 1484 * <jc>// Create a parser with write-only property settings.</jc> 1485 * ReaderParser <jv>parser</jv> = JsonParser 1486 * .<jsm>create</jsm>() 1487 * .beanPropertiesWriteOnly(AMap.<jsm>of</jsm>(<js>"MyBean"</js>, <js>"bar,baz"</js>)) 1488 * .build(); 1489 * 1490 * <jc>// Parser parses all 3 properties.</jc> 1491 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{foo:'foo',bar:'bar',baz:'baz'}"</js>, MyBean.<jk>class</jk>); 1492 * </p> 1493 * 1494 * <p> 1495 * This method is functionally equivalent to the following code for each entry: 1496 * <p class='bjava'> 1497 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>key</jv>).writeOnlyProperties(<jv>value</jv>.toString()).build()); 1498 * </p> 1499 * 1500 * <h5 class='section'>See Also:</h5><ul> 1501 * <li class='jma'>{@link BeanType#writeOnlyProperties()} / {@link BeanType#wo()} 1502 * </ul> 1503 * 1504 * @param values 1505 * The values to add to this builder. 1506 * <br>Keys are bean class names which can be a simple name, fully-qualified name, or <js>"*"</js> for all beans. 1507 * <br>Values are comma-delimited lists of property names. Non-String objects are first converted to Strings. 1508 * <br>Cannot be <jk>null</jk>. 1509 * @return This object. 1510 */ 1511 public Builder beanPropertiesWriteOnly(Map<String,Object> values) { 1512 reqnn("values", values); 1513 values.forEach((k, v) -> annotations(BeanTypeApplyAnnotation.create(k).value(BeanTypeAnnotation.create().wo(s(v)).build()).build())); 1514 return this; 1515 } 1516 1517 /** 1518 * Write-only bean properties. 1519 * 1520 * <p> 1521 * Specifies one or more properties on a bean that are write-only despite having valid setters. 1522 * Parsers will parse such properties as usual, but serializers will silently ignore them. 1523 * Note that this is different from the {@link #beanProperties(Class,String) beanProperties}/{@link #beanPropertiesExcludes(Class,String) beanPropertiesExcludes} settings which include or exclude properties 1524 * for both serializers and parsers. 1525 * 1526 * <h5 class='section'>Example:</h5> 1527 * <p class='bjava'> 1528 * <jc>// A bean with 3 properties.</jc> 1529 * <jk>public class</jk> MyBean { 1530 * <jk>public</jk> String <jf>foo</jf>, <jf>bar</jf>, <jf>baz</jf>; 1531 * } 1532 * 1533 * <jc>// Create a serializer with write-only property settings.</jc> 1534 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1535 * .<jsm>create</jsm>() 1536 * .beanPropertiesWriteOnly(<js>"MyBean"</js>, <js>"bar,baz"</js>) 1537 * .build(); 1538 * 1539 * <jc>// Only foo will be serialized.</jc> 1540 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1541 * 1542 * <jc>// Create a parser with write-only property settings.</jc> 1543 * ReaderParser <jv>parser</jv> = JsonParser 1544 * .<jsm>create</jsm>() 1545 * .beanPropertiesWriteOnly(<js>"MyBean"</js>, <js>"bar,baz"</js>) 1546 * .build(); 1547 * 1548 * <jc>// Parser parses all 3 properties.</jc> 1549 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{foo:'foo',bar:'bar',baz:'baz'}"</js>, MyBean.<jk>class</jk>); 1550 * </p> 1551 * 1552 * <p> 1553 * This method is functionally equivalent to the following code: 1554 * <p class='bjava'> 1555 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>beanClassName</jv>).writeOnlyProperties(<jv>properties</jv>).build()); 1556 * </p> 1557 * 1558 * <h5 class='section'>See Also:</h5><ul> 1559 * <li class='jma'>{@link BeanType#writeOnlyProperties()} / {@link BeanType#wo()} 1560 * </ul> 1561 * 1562 * @param beanClassName 1563 * The bean class name. 1564 * <br>Can be a simple name, fully-qualified name, or <js>"*"</js> for all bean classes. 1565 * @param properties Comma-delimited list of property names. 1566 * @return This object. 1567 */ 1568 public Builder beanPropertiesWriteOnly(String beanClassName, String properties) { 1569 reqnn("beanClassName", beanClassName); 1570 reqnn("properties", properties); 1571 return annotations(BeanTypeApplyAnnotation.create(beanClassName).value(BeanTypeAnnotation.create().wo(properties).build()).build()); 1572 } 1573 1574 /** 1575 * Beans require no-arg constructors. 1576 * 1577 * <p> 1578 * When enabled, a Java class must implement a default no-arg constructor to be considered a bean. 1579 * Otherwise, the bean will be serialized as a string using the {@link Object#toString()} method. 1580 * 1581 * <h5 class='section'>Example:</h5> 1582 * <p class='bjava'> 1583 * <jc>// A bean without a no-arg constructor.</jc> 1584 * <jk>public class</jk> MyBean { 1585 * 1586 * <jc>// A property method.</jc> 1587 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 1588 * 1589 * <jc>// A no-arg constructor</jc> 1590 * <jk>public</jk> MyBean(String <jv>foo</jv>) { 1591 * <jk>this</jk>.<jf>foo</jf> = <jv>foo</jv>; 1592 * } 1593 * 1594 * <ja>@Override</ja> 1595 * <jk>public</jk> String toString() { 1596 * <jk>return</jk> <js>"bar"</js>; 1597 * } 1598 * } 1599 * 1600 * <jc>// Create a serializer that ignores beans without default constructors.</jc> 1601 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1602 * .<jsm>create</jsm>() 1603 * .beansRequireDefaultConstructor() 1604 * .build(); 1605 * 1606 * <jc>// Produces: "bar"</jc> 1607 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1608 * </p> 1609 * 1610 * <h5 class='section'>Notes:</h5><ul> 1611 * <li class='note'>The {@link Bean @Marshalled} annotation can be used on a bean class to override this setting. 1612 * <li class='note'>The {@link BeanIgnore @BeanIgnore} annotation can also be used on a class to ignore it as a bean. 1613 * </ul> 1614 * 1615 * <h5 class='section'>See Also:</h5><ul> 1616 * <li class='ja'>{@link BeanConfig#beansRequireDefaultConstructor()} 1617 * </ul> 1618 * 1619 * @return This object. 1620 */ 1621 public Builder beansRequireDefaultConstructor() { 1622 return beansRequireDefaultConstructor(true); 1623 } 1624 1625 /** 1626 * Same as {@link #beansRequireDefaultConstructor()} but allows you to explicitly specify the value. 1627 * 1628 * @param value The value for this setting. 1629 * @return This object. 1630 */ 1631 public Builder beansRequireDefaultConstructor(boolean value) { 1632 beansRequireDefaultConstructor = value; 1633 return this; 1634 } 1635 1636 /** 1637 * Beans require Serializable interface. 1638 * 1639 * <p> 1640 * When enabled, a Java class must implement the {@link Serializable} interface to be considered a bean. 1641 * Otherwise, the bean will be serialized as a string using the {@link Object#toString()} method. 1642 * 1643 * <h5 class='section'>Example:</h5> 1644 * <p class='bjava'> 1645 * <jc>// A bean without a Serializable interface.</jc> 1646 * <jk>public class</jk> MyBean { 1647 * 1648 * <jc>// A property method.</jc> 1649 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 1650 * 1651 * <ja>@Override</ja> 1652 * <jk>public</jk> String toString() { 1653 * <jk>return</jk> <js>"bar"</js>; 1654 * } 1655 * } 1656 * 1657 * <jc>// Create a serializer that ignores beans not implementing Serializable.</jc> 1658 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1659 * .<jsm>create</jsm>() 1660 * .beansRequireSerializable() 1661 * .build(); 1662 * 1663 * <jc>// Produces: "bar"</jc> 1664 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1665 * </p> 1666 * 1667 * <h5 class='section'>Notes:</h5><ul> 1668 * <li class='note'>The {@link Bean @Marshalled} annotation can be used on a bean class to override this setting. 1669 * <li class='note'>The {@link BeanIgnore @BeanIgnore} annotation can also be used on a class to ignore it as a bean. 1670 * </ul> 1671 * 1672 * <h5 class='section'>See Also:</h5><ul> 1673 * <li class='ja'>{@link BeanConfig#beansRequireSerializable()} 1674 * </ul> 1675 * 1676 * @return This object. 1677 */ 1678 public Builder beansRequireSerializable() { 1679 return beansRequireSerializable(true); 1680 } 1681 1682 /** 1683 * Same as {@link #beansRequireSerializable()} but allows you to explicitly specify the value. 1684 * 1685 * @param value The value for this setting. 1686 * @return This object. 1687 */ 1688 public Builder beansRequireSerializable(boolean value) { 1689 beansRequireSerializable = value; 1690 return this; 1691 } 1692 1693 /** 1694 * Beans require setters for getters. 1695 * 1696 * <p> 1697 * When enabled, ignore read-only properties (properties with getters but not setters). 1698 * 1699 * <h5 class='section'>Example:</h5> 1700 * <p class='bjava'> 1701 * <jc>// A bean without a Serializable interface.</jc> 1702 * <jk>public class</jk> MyBean { 1703 * 1704 * <jc>// A read/write property.</jc> 1705 * <jk>public</jk> String getFoo() { <jk>return</jk> <js>"foo"</js>; } 1706 * <jk>public void</jk> setFoo(String <jv>foo</jv>) { ... } 1707 * 1708 * <jc>// A read-only property.</jc> 1709 * <jk>public</jk> String getBar() { <jk>return</jk> <js>"bar"</js>; } 1710 * } 1711 * 1712 * <jc>// Create a serializer that ignores bean properties without setters.</jc> 1713 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1714 * .<jsm>create</jsm>() 1715 * .beansRequireSettersForGetters() 1716 * .build(); 1717 * 1718 * <jc>// Produces: {"foo":"foo"}</jc> 1719 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1720 * </p> 1721 * 1722 * <h5 class='section'>Notes:</h5><ul> 1723 * <li class='note'>The {@link BeanProp @BeanProp} annotation can be used on the getter to override this setting. 1724 * <li class='note'>The {@link BeanIgnore @BeanIgnore} annotation can also be used on getters to ignore them as bean properties. 1725 * </ul> 1726 * 1727 * @return This object. 1728 */ 1729 public Builder beansRequireSettersForGetters() { 1730 return beansRequireSettersForGetters(true); 1731 } 1732 1733 /** 1734 * Same as {@link #beansRequireSettersForGetters()} but allows you to explicitly specify the value. 1735 * 1736 * @param value The value for this setting. 1737 * @return This object. 1738 */ 1739 public Builder beansRequireSettersForGetters(boolean value) { 1740 beansRequireSettersForGetters = value; 1741 return this; 1742 } 1743 1744 /** 1745 * Sets the bean store used for factory-based instantiation. 1746 * 1747 * <p> 1748 * The bean store is used to resolve {@link BeanFactory} instances registered via 1749 * <ja>@BeanType</ja><c>(factory=X.class)</c> and <ja>@BeanProp</ja><c>(factory=X.class)</c> annotations. 1750 * When a factory class is encountered, the framework first looks it up in the bean store 1751 * before attempting direct instantiation. 1752 * 1753 * <p> 1754 * Typically set to a {@code SpringBeanStore} wrapping the application's 1755 * {@code ApplicationContext} so that Spring-managed factories are resolved automatically. 1756 * 1757 * <h5 class='section'>Example:</h5> 1758 * <p class='bjava'> 1759 * <jk>public class</jk> MyRestServlet <jk>extends</jk> BasicRestServlet { 1760 * <ja>@Autowired</ja> ApplicationContext <jv>ctx</jv>; 1761 * 1762 * <ja>@Override</ja> 1763 * <jk>protected</jk> MarshallingContext createBeanContext(MarshallingContext.Builder<?> <jv>builder</jv>) { 1764 * <jk>return</jk> <jv>builder</jv>.beanStore(<jk>new</jk> SpringBeanStore(<jv>ctx</jv>)).build(); 1765 * } 1766 * } 1767 * </p> 1768 * 1769 * @param value The bean store, or <jk>null</jk> to use direct instantiation only. 1770 * @return This object. 1771 */ 1772 public Builder beanStore(BeanStore value) { 1773 beanStore = value; 1774 return this; 1775 } 1776 1777 @Override /* Overridden from Context.Builder<?> */ 1778 public MarshallingContext build() { 1779 return cache(CACHE).build(MarshallingContext.class); 1780 } 1781 1782 @Override /* Overridden from Context.Builder<?> */ 1783 public Builder copy() { 1784 return new Builder(this); 1785 } 1786 1787 /** 1788 * Null-safe copy: returns a deep copy of the specified value, or <jk>null</jk> if the value is <jk>null</jk>. 1789 * 1790 * @param value The value to copy. Can be <jk>null</jk> (returns <jk>null</jk>). 1791 * @return A copy of the value, or <jk>null</jk> if the value was <jk>null</jk>. 1792 */ 1793 public static Builder copyOrNull(Builder value) { 1794 return value == null ? null : value.copy(); 1795 } 1796 1797 /** 1798 * Bean dictionary. 1799 * 1800 * <p> 1801 * This is identical to {@link #beanDictionary(Class...)}, but specifies a dictionary within the context of 1802 * a single class as opposed to globally. 1803 * 1804 * <h5 class='section'>Example:</h5> 1805 * <p class='bjava'> 1806 * <jc>// POJOs with @Marshalled(name) annotations.</jc> 1807 * <ja>@Marshalled</ja>(typeName=<js>"foo"</js>) 1808 * <jk>public class</jk> Foo {...} 1809 * <ja>@Marshalled</ja>(typeName=<js>"bar"</js>) 1810 * <jk>public class</jk> Bar {...} 1811 * 1812 * <jc>// A bean with a field with an indeterminate type.</jc> 1813 * <jk>public class</jk> MyBean { 1814 * <jk>public</jk> Object <jf>mySimpleField</jf>; 1815 * } 1816 * 1817 * <jc>// Create a parser and tell it which classes to try to resolve.</jc> 1818 * ReaderParser <jv>parser</jv> = JsonParser 1819 * .<jsm>create</jsm>() 1820 * .dictionaryOn(MyBean.<jk>class</jk>, Foo.<jk>class</jk>, Bar.<jk>class</jk>) 1821 * .build(); 1822 * 1823 * <jc>// Parse bean.</jc> 1824 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{mySimpleField:{_type:'foo',...}}"</js>, MyBean.<jk>class</jk>); 1825 * </p> 1826 * 1827 * <p> 1828 * This is functionally equivalent to the {@link Marshalled#dictionary()} annotation. 1829 * 1830 * <h5 class='section'>See Also:</h5><ul> 1831 * <li class='jma'>{@link Marshalled#dictionary()} 1832 * <li class='jm'>{@link #beanDictionary(Class...)} 1833 * </ul> 1834 * 1835 * @param on The class that the dictionary values apply to. 1836 * @param values 1837 * The new values for this setting. 1838 * @return This object. 1839 */ 1840 public Builder dictionaryOn(Class<?> on, Class<?>...values) { 1841 reqnn("on", on); 1842 reqnns("values", values); 1843 return annotations(MarshalledApplyAnnotation.create(on).value(MarshalledAnnotation.create().dictionary(values).build()).build()); 1844 } 1845 1846 /** 1847 * Beans don't require at least one property. 1848 * 1849 * <p> 1850 * When enabled, then a Java class doesn't need to contain at least 1 property to be considered a bean. 1851 * Otherwise, the bean will be serialized as a string using the {@link Object#toString()} method. 1852 * 1853 * <p> 1854 * The {@link Bean @Marshalled} annotation can be used on a class to override this setting when <jk>true</jk>. 1855 * 1856 * <h5 class='section'>Example:</h5> 1857 * <p class='bjava'> 1858 * <jc>// A bean with no properties.</jc> 1859 * <jk>public class</jk> MyBean { 1860 * } 1861 * 1862 * <jc>// Create a serializer that serializes beans even if they have zero properties.</jc> 1863 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1864 * .<jsm>create</jsm>() 1865 * .disableBeansRequireSomeProperties() 1866 * .build(); 1867 * 1868 * <jc>// Produces: {}</jc> 1869 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1870 * </p> 1871 * 1872 * <h5 class='section'>Notes:</h5><ul> 1873 * <li class='note'>The {@link Bean @Marshalled} annotation can be used on the class to force it to be recognized as a bean class 1874 * even if it has no properties. 1875 * </ul> 1876 * 1877 * <h5 class='section'>See Also:</h5><ul> 1878 * <li class='ja'>{@link BeanConfig#disableBeansRequireSomeProperties()} 1879 * </ul> 1880 * 1881 * @return This object. 1882 */ 1883 public Builder disableBeansRequireSomeProperties() { 1884 return disableBeansRequireSomeProperties(true); 1885 } 1886 1887 /** 1888 * Same as {@link #disableBeansRequireSomeProperties()} but allows you to explicitly specify the value. 1889 * 1890 * @param value The value for this setting. 1891 * @return This object. 1892 */ 1893 public Builder disableBeansRequireSomeProperties(boolean value) { 1894 disableBeansRequireSomeProperties = value; 1895 return this; 1896 } 1897 1898 /** 1899 * Don't silently ignore missing setters. 1900 * 1901 * <p> 1902 * When enabled, trying to set a value on a bean property without a setter will throw a {@link BeanRuntimeException}. 1903 * Otherwise, it will be silently ignored. 1904 * 1905 * <h5 class='section'>Example:</h5> 1906 * <p class='bjava'> 1907 * <jc>// A bean with a property with a getter but not a setter.</jc> 1908 * <jk>public class</jk> MyBean { 1909 * <jk>public void</jk> getFoo() { 1910 * <jk>return</jk> <js>"foo"</js>; 1911 * } 1912 * } 1913 * 1914 * <jc>// Create a parser that throws an exception if a setter is not found but a getter is.</jc> 1915 * ReaderParser <jv>parser</jv> = JsonParser 1916 * .<jsm>create</jsm>() 1917 * .disableIgnoreMissingSetters() 1918 * .build(); 1919 * 1920 * <jc>// Throws a ParseException.</jc> 1921 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{foo:'bar'}"</js>, MyBean.<jk>class</jk>); 1922 * </p> 1923 * 1924 * <h5 class='section'>Notes:</h5><ul> 1925 * <li class='note'>The {@link BeanIgnore @BeanIgnore} annotation can also be used on getters and fields to ignore them. 1926 * </ul> 1927 * 1928 * <h5 class='section'>See Also:</h5><ul> 1929 * <li class='ja'>{@link BeanConfig#disableIgnoreMissingSetters()} 1930 * </ul> 1931 * 1932 * @return This object. 1933 */ 1934 public Builder disableIgnoreMissingSetters() { 1935 return disableIgnoreMissingSetters(true); 1936 } 1937 1938 /** 1939 * Same as {@link #disableIgnoreMissingSetters()} but allows you to explicitly specify the value. 1940 * 1941 * @param value The value for this setting. 1942 * @return This object. 1943 */ 1944 public Builder disableIgnoreMissingSetters(boolean value) { 1945 disableIgnoreMissingSetters = value; 1946 return this; 1947 } 1948 1949 /** 1950 * Don't ignore transient fields. 1951 * 1952 * <p> 1953 * When enabled, methods and fields marked as <jk>transient</jk> will not be ignored as bean properties. 1954 * 1955 * <h5 class='section'>Example:</h5> 1956 * <p class='bjava'> 1957 * <jc>// A bean with a transient field.</jc> 1958 * <jk>public class</jk> MyBean { 1959 * <jk>public transient</jk> String <jf>foo</jf> = <js>"foo"</js>; 1960 * } 1961 * 1962 * <jc>// Create a serializer that doesn't ignore transient fields.</jc> 1963 * WriterSerializer <jv>serializer</jv> = JsonSerializer 1964 * .<jsm>create</jsm>() 1965 * .disableIgnoreTransientFields() 1966 * .build(); 1967 * 1968 * <jc>// Produces: {"foo":"foo"}</jc> 1969 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 1970 * </p> 1971 * 1972 * <h5 class='section'>Notes:</h5><ul> 1973 * <li class='note'>The {@link BeanProp @BeanProp} annotation can also be used on transient fields to keep them from being ignored. 1974 * </ul> 1975 * 1976 * <h5 class='section'>See Also:</h5><ul> 1977 * <li class='ja'>{@link BeanConfig#disableIgnoreTransientFields()} 1978 * </ul> 1979 * 1980 * @return This object. 1981 */ 1982 public Builder disableIgnoreTransientFields() { 1983 return disableIgnoreTransientFields(true); 1984 } 1985 1986 /** 1987 * Same as {@link #disableIgnoreTransientFields()} but allows you to explicitly specify the value. 1988 * 1989 * @param value The value for this setting. 1990 * @return This object. 1991 */ 1992 public Builder disableIgnoreTransientFields(boolean value) { 1993 disableIgnoreTransientFields = value; 1994 return this; 1995 } 1996 1997 /** 1998 * Don't ignore unknown properties with null values. 1999 * 2000 * <p> 2001 * When enabled, trying to set a <jk>null</jk> value on a non-existent bean property will throw a {@link BeanRuntimeException}. 2002 * Otherwise it will be silently ignored. 2003 * 2004 * <h5 class='section'>Example:</h5> 2005 * <p class='bjava'> 2006 * <jc>// A bean with a single property.</jc> 2007 * <jk>public class</jk> MyBean { 2008 * <jk>public</jk> String <jf>foo</jf>; 2009 * } 2010 * 2011 * <jc>// Create a parser that throws an exception on an unknown property even if the value being set is null.</jc> 2012 * ReaderParser <jv>parser</jv> = JsonParser 2013 * .<jsm>create</jsm>() 2014 * .disableIgnoreUnknownNullBeanProperties() 2015 * .build(); 2016 * 2017 * <jc>// Throws a BeanRuntimeException wrapped in a ParseException on the unknown 'bar' property.</jc> 2018 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{foo:'foo',bar:null}"</js>, MyBean.<jk>class</jk>); 2019 * </p> 2020 * 2021 * <h5 class='section'>See Also:</h5><ul> 2022 * <li class='ja'>{@link BeanConfig#disableIgnoreUnknownNullBeanProperties()} 2023 * </ul> 2024 * 2025 * @return This object. 2026 */ 2027 public Builder disableIgnoreUnknownNullBeanProperties() { 2028 return disableIgnoreUnknownNullBeanProperties(true); 2029 } 2030 2031 /** 2032 * Same as {@link #disableIgnoreUnknownNullBeanProperties()} but allows you to explicitly specify the value. 2033 * 2034 * @param value The value for this setting. 2035 * @return This object. 2036 */ 2037 public Builder disableIgnoreUnknownNullBeanProperties(boolean value) { 2038 disableIgnoreUnknownNullBeanProperties = value; 2039 return this; 2040 } 2041 2042 /** 2043 * Don't use interface proxies. 2044 * 2045 * <p> 2046 * When enabled, interfaces will be instantiated as proxy classes through the use of an 2047 * {@link InvocationHandler} if there is no other way of instantiating them. 2048 * Otherwise, throws a {@link BeanRuntimeException}. 2049 * 2050 * <h5 class='section'>See Also:</h5><ul> 2051 * <li class='ja'>{@link BeanConfig#disableInterfaceProxies()} 2052 * </ul> 2053 * 2054 * @return This object. 2055 */ 2056 public Builder disableInterfaceProxies() { 2057 return disableInterfaceProxies(true); 2058 } 2059 2060 /** 2061 * Same as {@link #disableInterfaceProxies()} but allows you to explicitly specify the value. 2062 * 2063 * @param value The value for this setting. 2064 * @return This object. 2065 */ 2066 public Builder disableInterfaceProxies(boolean value) { 2067 disableInterfaceProxies = value; 2068 return this; 2069 } 2070 2071 /** 2072 * POJO example. 2073 * 2074 * <p> 2075 * Specifies an example in JSON of the specified class. 2076 * 2077 * <p> 2078 * Examples are used in cases such as POJO examples in Swagger documents. 2079 * 2080 * <p> 2081 * Setting applies to specified class and all subclasses. 2082 * 2083 * <h5 class='section'>Example:</h5> 2084 * <p class='bjava'> 2085 * <jc>// Create a serializer that excludes the 'foo' and 'bar' properties on the MyBean class.</jc> 2086 * WriterSerializer <jv>serializer</jv> = JsonSerializer 2087 * .<jsm>create</jsm>() 2088 * .example(MyBean.<jk>class</jk>, <js>"{foo:'bar'}"</js>) 2089 * .build(); 2090 * </p> 2091 * 2092 * <p> 2093 * This is a shorthand method for the following code: 2094 * <p class='bjava'> 2095 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>pojoClass</jv>).example(<jv>json</jv>).build()) 2096 * </p> 2097 * 2098 * <p> 2099 * POJO examples can also be defined on classes via the following: 2100 * <ul class='spaced-list'> 2101 * <li>A static field annotated with {@link Example @Example}. 2102 * <li>A static method annotated with {@link Example @Example} with zero arguments or one {@link MarshallingSession} argument. 2103 * <li>A static method with name <c>example</c> with no arguments or one {@link MarshallingSession} argument. 2104 * </ul> 2105 * 2106 * <h5 class='section'>See Also:</h5><ul> 2107 * <li class='ja'>{@link Marshalled#example()} 2108 * </ul> 2109 * 2110 * @param <T> The POJO class type. 2111 * @param pojoClass The POJO class. 2112 * <br>Cannot be <jk>null</jk>. 2113 * @param json The JSON 5 representation of the example. 2114 * <br>Can be <jk>null</jk> or empty (treated as no example). 2115 * @return This object. 2116 */ 2117 public <T> Builder example(Class<T> pojoClass, String json) { 2118 return annotations(MarshalledApplyAnnotation.create(reqnn("pojoClass", pojoClass)).value(MarshalledAnnotation.create().example(json).build()).build()); 2119 } 2120 2121 /** 2122 * POJO example. 2123 * 2124 * <p> 2125 * Specifies an example of the specified class. 2126 * 2127 * <p> 2128 * Examples are used in cases such as POJO examples in Swagger documents. 2129 * 2130 * <h5 class='section'>Example:</h5> 2131 * <p class='bjava'> 2132 * <jc>// Create a serializer that excludes the 'foo' and 'bar' properties on the MyBean class.</jc> 2133 * WriterSerializer <jv>serializer</jv> = JsonSerializer 2134 * .<jsm>create</jsm>() 2135 * .example(MyBean.<jk>class</jk>, <jk>new</jk> MyBean().setFoo(<js>"foo"</js>).setBar(123)) 2136 * .build(); 2137 * </p> 2138 * 2139 * <p> 2140 * This is a shorthand method for the following code: 2141 * <p class='bjava'> 2142 * <jv>builder</jv>.annotations(MarshalledAnnotation.<jsm>create</jsm>(<jv>pojoClass</jv>).example(Json5.<jsf>DEFAULT</jsf>.toString(<jv>object</jv>)).build()) 2143 * </p> 2144 * 2145 * <h5 class='section'>Notes:</h5><ul> 2146 * <li class='note'>Using this method assumes the serialized form of the object is the same as that produced 2147 * by the default serializer. This may not be true based on settings or swaps on the constructed serializer. 2148 * </ul> 2149 * 2150 * <p> 2151 * POJO examples can also be defined on classes via the following: 2152 * <ul class='spaced-list'> 2153 * <li>The {@link Marshalled#example()} annotation on the class itself. 2154 * <li>A static field annotated with {@link Example @Example}. 2155 * <li>A static method annotated with {@link Example @Example} with zero arguments or one {@link MarshallingSession} argument. 2156 * <li>A static method with name <c>example</c> with no arguments or one {@link MarshallingSession} argument. 2157 * </ul> 2158 * 2159 * @param <T> The POJO class. 2160 * @param pojoClass The POJO class. 2161 * <br>Cannot be <jk>null</jk>. 2162 * @param o 2163 * An instance of the POJO class used for examples. 2164 * <br>Can be <jk>null</jk> (will be serialized as <js>"null"</js>). 2165 * @return This object. 2166 */ 2167 public <T> Builder example(Class<T> pojoClass, T o) { 2168 return annotations(MarshalledApplyAnnotation.create(reqnn("pojoClass", pojoClass)).value(MarshalledAnnotation.create().example(Json5.of(o)).build()).build()); 2169 } 2170 2171 /** 2172 * Find fluent setters. 2173 * 2174 * <p> 2175 * When enabled, fluent setters are detected on beans during parsing. 2176 * 2177 * <p> 2178 * Fluent setters must have the following attributes: 2179 * <ul> 2180 * <li>Public. 2181 * <li>Not static. 2182 * <li>Take in one parameter. 2183 * <li>Return the bean itself. 2184 * </ul> 2185 * 2186 * <h5 class='section'>Example:</h5> 2187 * <p class='bjava'> 2188 * <jc>// A bean with a fluent setter.</jc> 2189 * <jk>public class</jk> MyBean { 2190 * <jk>public</jk> MyBean foo(String <jv>value</jv>) {...} 2191 * } 2192 * 2193 * <jc>// Create a parser that finds fluent setters.</jc> 2194 * ReaderParser <jv>parser</jv> = JsonParser 2195 * .<jsm>create</jsm>() 2196 * .findFluentSetters() 2197 * .build(); 2198 * 2199 * <jc>// Parse into bean using fluent setter.</jc> 2200 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{foo:'bar'}"</js>); 2201 * </p> 2202 * 2203 * <h5 class='section'>Notes:</h5><ul> 2204 * <li class='note'>The {@link BeanProp @BeanProp} annotation can also be used on methods to individually identify them as fluent setters. 2205 * <li class='note'>The {@link BeanType#findFluentSetters() @BeanType.findFluentSetters()} annotation can also be used on classes to specify to look for fluent setters. 2206 * </ul> 2207 * 2208 * <h5 class='section'>See Also:</h5><ul> 2209 * <li class='ja'>{@link BeanType#findFluentSetters()} 2210 * <li class='ja'>{@link BeanConfig#findFluentSetters()} 2211 * </ul> 2212 * 2213 * @return This object. 2214 */ 2215 public Builder findFluentSetters() { 2216 return findFluentSetters(true); 2217 } 2218 2219 /** 2220 * Same as {@link #findFluentSetters()} but allows you to explicitly specify the value. 2221 * 2222 * @param value The value for this setting. 2223 * @return This object. 2224 */ 2225 public Builder findFluentSetters(boolean value) { 2226 findFluentSetters = value; 2227 return this; 2228 } 2229 2230 /** 2231 * Find fluent setters. 2232 * 2233 * <p> 2234 * Identical to {@link #findFluentSetters()} but enables it on a specific class only. 2235 * 2236 * <h5 class='section'>Example:</h5> 2237 * <p class='bjava'> 2238 * <jc>// A bean with a fluent setter.</jc> 2239 * <jk>public class</jk> MyBean { 2240 * <jk>public</jk> MyBean foo(String <jv>value</jv>) {...} 2241 * } 2242 * 2243 * <jc>// Create a parser that finds fluent setters.</jc> 2244 * ReaderParser <jv>parser</jv> = JsonParser 2245 * .<jsm>create</jsm>() 2246 * .findFluentSetters(MyBean.<jk>class</jk>) 2247 * .build(); 2248 * 2249 * <jc>// Parse into bean using fluent setter.</jc> 2250 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{foo:'bar'}"</js>); 2251 * </p> 2252 * 2253 * <h5 class='section'>Notes:</h5><ul> 2254 * <li class='note'>This method is functionally equivalent to using the {@link BeanType#findFluentSetters()} annotation. 2255 * </ul> 2256 * 2257 * <h5 class='section'>See Also:</h5><ul> 2258 * <li class='ja'>{@link BeanType#findFluentSetters()} 2259 * <li class='jm'>{@link #findFluentSetters()} 2260 * </ul> 2261 * 2262 * @param on The class that this applies to. 2263 * <br>Cannot be <jk>null</jk>. 2264 * @return This object. 2265 */ 2266 public Builder findFluentSetters(Class<?> on) { 2267 reqnn("on", on); 2268 return annotations(BeanTypeApplyAnnotation.create(on).value(BeanTypeAnnotation.create().findFluentSetters(true).build()).build()); 2269 } 2270 2271 @Override /* Overridden from Context.Builder<?> */ 2272 public HashKey hashKey() { 2273 // @formatter:off 2274 return HashKey.of( 2275 super.hashKey(), 2276 beanClassVisibility, 2277 beanConstructorVisibility, 2278 beanMethodVisibility, 2279 beanFieldVisibility, 2280 beanDictionary, 2281 swaps, 2282 notBeanClasses, 2283 notBeanPackages, 2284 integer( 2285 disableBeansRequireSomeProperties, 2286 beanMapPutReturnsOldValue, 2287 beansRequireDefaultConstructor, 2288 beansRequireSerializable, 2289 beansRequireSettersForGetters, 2290 disableIgnoreTransientFields, 2291 disableIgnoreUnknownNullBeanProperties, 2292 disableIgnoreMissingSetters, 2293 disableInterfaceProxies, 2294 findFluentSetters, 2295 ignoreInvocationExceptionsOnGetters, 2296 ignoreInvocationExceptionsOnSetters, 2297 ignoreUnknownBeanProperties, 2298 ignoreUnknownEnumValues, 2299 recordComponentOrder, 2300 unsortedProperties, 2301 useJavaBeanIntrospector, 2302 validateSchema, 2303 disableDefaultViewInclusion 2304 ), 2305 activeView, 2306 typePropertyName, 2307 mediaType, 2308 timeZone, 2309 durationFormat, 2310 periodFormat, 2311 calendarFormat, 2312 dateFormat, 2313 temporalFormat, 2314 timeZoneFormat, 2315 localeFormat, 2316 binaryFormat, 2317 enumFormat, 2318 uuidFormat, 2319 bitSetFormat, 2320 bigNumberFormat, 2321 booleanFormat, 2322 floatFormat, 2323 currencyFormat, 2324 classFormat, 2325 locale, 2326 propertyNamer, 2327 System.identityHashCode(beanStore), 2328 System.identityHashCode(classLoader) 2329 ); 2330 // @formatter:on 2331 } 2332 2333 /** 2334 * Sets the default active view name for all sessions created from this context. 2335 * 2336 * <p> 2337 * When set, only bean properties whose declared {@link MarshalledProp#view()} set contains this view name 2338 * will be included during serialization. On the parse side, properties outside the active view are routed 2339 * through the existing unknown/ignored-property mechanism governed by 2340 * {@link #ignoreUnknownBeanProperties()}. 2341 * 2342 * <p> 2343 * Untagged properties (those whose declared view set is empty) follow the default-view-inclusion policy: 2344 * included under every active view unless {@link #disableDefaultViewInclusion()} has been called. 2345 * 2346 * <p> 2347 * This value can be overridden per serialization or parse call via the session builder (e.g. 2348 * {@link MarshallingSession.Builder#activeView(String)}). 2349 * 2350 * <h5 class='section'>Example:</h5> 2351 * <p class='bjava'> 2352 * JsonSerializer <jv>serializer</jv> = JsonSerializer.<jsm>create</jsm>() 2353 * .activeView(<js>"summary"</js>) 2354 * .build(); 2355 * </p> 2356 * 2357 * <h5 class='section'>See Also:</h5><ul> 2358 * <li class='ja'>{@link MarshalledProp#view()} 2359 * <li class='jm'>{@link MarshallingSession.Builder#activeView(String)} 2360 * </ul> 2361 * 2362 * @param value The active view name. Use <jk>null</jk> to disable view filtering. 2363 * @return This object. 2364 * @since 10.0.0 2365 */ 2366 public Builder activeView(String value) { 2367 activeView = value; 2368 return this; 2369 } 2370 2371 /** 2372 * Disables the default-view-inclusion policy. 2373 * 2374 * <p> 2375 * By default, bean properties that carry no {@link MarshalledProp#view()} declaration are included under 2376 * every active view (matching Jackson's {@code DEFAULT_VIEW_INCLUSION} behavior). 2377 * Calling this method reverses the policy: untagged properties are <em>excluded</em> when any active view 2378 * is set. 2379 * 2380 * <h5 class='section'>See Also:</h5><ul> 2381 * <li class='ja'>{@link MarshalledProp#view()} 2382 * <li class='jm'>{@link #activeView(String)} 2383 * </ul> 2384 * 2385 * @return This object. 2386 * @since 10.0.0 2387 */ 2388 public Builder disableDefaultViewInclusion() { 2389 return disableDefaultViewInclusion(true); 2390 } 2391 2392 /** 2393 * Same as {@link #disableDefaultViewInclusion()} but allows you to explicitly specify the value. 2394 * 2395 * @param value The value for this setting. 2396 * @return This object. 2397 * @since 10.0.0 2398 */ 2399 public Builder disableDefaultViewInclusion(boolean value) { 2400 disableDefaultViewInclusion = value; 2401 return this; 2402 } 2403 2404 /** 2405 * Ignore invocation errors on getters. 2406 * 2407 * <p> 2408 * When enabled, errors thrown when calling bean getter methods will silently be ignored. 2409 * Otherwise, a {@code BeanRuntimeException} is thrown. 2410 * 2411 * <h5 class='section'>Example:</h5> 2412 * <p class='bjava'> 2413 * <jc>// A bean with a property that throws an exception.</jc> 2414 * <jk>public class</jk> MyBean { 2415 * <jk>public</jk> String getFoo() { 2416 * <jk>throw new</jk> RuntimeException(<js>"foo"</js>); 2417 * } 2418 * } 2419 * 2420 * <jc>// Create a serializer that ignores bean getter exceptions.</jc> 2421 * WriterSerializer <jv>serializer</jv> = JsonSerializer 2422 * .<jsm>create</jsm>() 2423 * .ingoreInvocationExceptionsOnGetters() 2424 * .build(); 2425 * 2426 * <jc>// Exception is ignored.</jc> 2427 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 2428 * </p> 2429 * 2430 * <h5 class='section'>See Also:</h5><ul> 2431 * <li class='ja'>{@link BeanConfig#ignoreInvocationExceptionsOnGetters()} 2432 * </ul> 2433 * 2434 * @return This object. 2435 */ 2436 public Builder ignoreInvocationExceptionsOnGetters() { 2437 return ignoreInvocationExceptionsOnGetters(true); 2438 } 2439 2440 /** 2441 * Same as {@link #ignoreInvocationExceptionsOnGetters()} but allows you to explicitly specify the value. 2442 * 2443 * @param value The value for this setting. 2444 * @return This object. 2445 */ 2446 public Builder ignoreInvocationExceptionsOnGetters(boolean value) { 2447 ignoreInvocationExceptionsOnGetters = value; 2448 return this; 2449 } 2450 2451 /** 2452 * Ignore invocation errors on setters. 2453 * 2454 * <p> 2455 * When enabled, errors thrown when calling bean setter methods will silently be ignored. 2456 * Otherwise, a {@code BeanRuntimeException} is thrown. 2457 * 2458 * <h5 class='section'>Example:</h5> 2459 * <p class='bjava'> 2460 * <jc>// A bean with a property that throws an exception.</jc> 2461 * <jk>public class</jk> MyBean { 2462 * <jk>public void</jk> setFoo(String <jv>foo</jv>) { 2463 * <jk>throw new</jk> RuntimeException(<js>"foo"</js>); 2464 * } 2465 * } 2466 * 2467 * <jc>// Create a parser that ignores bean setter exceptions.</jc> 2468 * ReaderParser <jv>parser</jv> = JsonParser 2469 * .<jsm>create</jsm>() 2470 * .ignoreInvocationExceptionsOnSetters() 2471 * .build(); 2472 * 2473 * <jc>// Exception is ignored.</jc> 2474 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{foo:'bar'}"</js>, MyBean.<jk>class</jk>); 2475 * </p> 2476 * 2477 * <h5 class='section'>See Also:</h5><ul> 2478 * <li class='ja'>{@link BeanConfig#ignoreInvocationExceptionsOnSetters()} 2479 * </ul> 2480 * 2481 * @return This object. 2482 */ 2483 public Builder ignoreInvocationExceptionsOnSetters() { 2484 return ignoreInvocationExceptionsOnSetters(true); 2485 } 2486 2487 /** 2488 * Same as {@link #ignoreInvocationExceptionsOnSetters()} but allows you to explicitly specify the value. 2489 * 2490 * @param value The value for this setting. 2491 * @return This object. 2492 */ 2493 public Builder ignoreInvocationExceptionsOnSetters(boolean value) { 2494 ignoreInvocationExceptionsOnSetters = value; 2495 return this; 2496 } 2497 2498 /** 2499 * Ignore unknown properties. 2500 * 2501 * <p> 2502 * When enabled, trying to set a value on a non-existent bean property will silently be ignored. 2503 * Otherwise, a {@code BeanRuntimeException} is thrown. 2504 * 2505 * <h5 class='section'>Example:</h5> 2506 * <p class='bjava'> 2507 * <jc>// A bean with a single property.</jc> 2508 * <jk>public class</jk> MyBean { 2509 * <jk>public</jk> String <jf>foo</jf>; 2510 * } 2511 * 2512 * <jc>// Create a parser that ignores missing bean properties.</jc> 2513 * ReaderParser <jv>parser</jv> = JsonParser 2514 * .<jsm>create</jsm>() 2515 * .ignoreUnknownBeanProperties() 2516 * .build(); 2517 * 2518 * <jc>// Doesn't throw an exception on unknown 'bar' property.</jc> 2519 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"{foo:'foo',bar:'bar'}"</js>, MyBean.<jk>class</jk>); 2520 * </p> 2521 * 2522 * <h5 class='section'>See Also:</h5><ul> 2523 * <li class='ja'>{@link BeanConfig#ignoreUnknownBeanProperties()} 2524 * </ul> 2525 * 2526 * @return This object. 2527 */ 2528 public Builder ignoreUnknownBeanProperties() { 2529 return ignoreUnknownBeanProperties(true); 2530 } 2531 2532 /** 2533 * Same as {@link #ignoreUnknownBeanProperties()} but allows you to explicitly specify the value. 2534 * 2535 * @param value The value for this setting. 2536 * @return This object. 2537 */ 2538 public Builder ignoreUnknownBeanProperties(boolean value) { 2539 ignoreUnknownBeanProperties = value; 2540 return this; 2541 } 2542 2543 /** 2544 * Schema validation mode. 2545 * 2546 * <p> 2547 * When enabled, bean property values are validated against the constraints declared by 2548 * {@link Schema @Schema} annotations on those properties. Validation runs 2549 * during both <b>parsing</b> (value set on the bean) and <b>serialization</b> (value read from the bean). 2550 * 2551 * <p> 2552 * Backed by the typed {@code JsonSchema} bean in {@code juneau-bean-jsonschema} and JSON Schema Draft 2020-12 2553 * semantics. Validation failures throw {@link SchemaValidationException} 2554 * wrapped in {@code BeanRuntimeException}; the parser surfaces them as 2555 * {@link ParseException} and the serializer surfaces them as 2556 * {@link SerializeException}. 2557 * 2558 * <p> 2559 * If {@code juneau-bean-jsonschema} is not on the classpath, this flag becomes a silent no-op. 2560 * 2561 * <h5 class='section'>Example:</h5> 2562 * <p class='bjava'> 2563 * <jc>// A bean with a length-constrained property.</jc> 2564 * <jk>public class</jk> MyBean { 2565 * <ja>@Schema</ja>(minLength=<js>"2"</js>, maxLength=<js>"10"</js>) 2566 * <jk>public</jk> String <jf>name</jf>; 2567 * } 2568 * 2569 * <jc>// Parser that validates inbound values.</jc> 2570 * ReaderParser <jv>p</jv> = JsonParser.<jsm>create</jsm>().validateSchema().build(); 2571 * 2572 * <jv>p</jv>.read(<js>"{name:'a'}"</js>, MyBean.<jk>class</jk>); <jc>// throws ParseException - too short</jc> 2573 * </p> 2574 * 2575 * <h5 class='section'>See Also:</h5><ul> 2576 * <li class='ja'>{@link BeanConfig#validateSchema()} 2577 * </ul> 2578 * 2579 * @return This object. 2580 * @since 10.0.0 2581 */ 2582 public Builder validateSchema() { 2583 return validateSchema(true); 2584 } 2585 2586 /** 2587 * Same as {@link #validateSchema()} but allows you to explicitly specify the value. 2588 * 2589 * @param value The value for this setting. 2590 * @return This object. 2591 * @since 10.0.0 2592 */ 2593 public Builder validateSchema(boolean value) { 2594 validateSchema = value; 2595 return this; 2596 } 2597 2598 /** 2599 * Ignore unknown properties. 2600 * 2601 * <p> 2602 * When enabled, unknown enum values will be set to <jk>null</jk> instead of throwing an exception. 2603 * 2604 * <h5 class='section'>See Also:</h5><ul> 2605 * <li class='ja'>{@link BeanConfig#ignoreUnknownEnumValues()} 2606 * </ul> 2607 * 2608 * @return This object. 2609 */ 2610 public Builder ignoreUnknownEnumValues() { 2611 return ignoreUnknownEnumValues(true); 2612 } 2613 2614 /** 2615 * Same as {@link #ignoreUnknownEnumValues()} but allows you to explicitly specify the value. 2616 * 2617 * @param value The value for this setting. 2618 * @return This object. 2619 */ 2620 public Builder ignoreUnknownEnumValues(boolean value) { 2621 ignoreUnknownEnumValues = value; 2622 return this; 2623 } 2624 2625 /** 2626 * Implementation classes. 2627 * 2628 * <p> 2629 * For interfaces and abstract classes this method can be used to specify an implementation class for the 2630 * interface/abstract class so that instances of the implementation class are used when instantiated (e.g. during a 2631 * parse). 2632 * 2633 * <h5 class='section'>Example:</h5> 2634 * <p class='bjava'> 2635 * <jc>// A bean interface.</jc> 2636 * <jk>public interface</jk> MyBean { 2637 * ... 2638 * } 2639 * 2640 * <jc>// A bean implementation.</jc> 2641 * <jk>public class</jk> MyBeanImpl <jk>implements</jk> MyBean { 2642 * ... 2643 * } 2644 2645 * <jc>// Create a parser that instantiates MyBeanImpls when parsing MyBeans.</jc> 2646 * ReaderParser <jv>parser</jv> = JsonParser 2647 * .<jsm>create</jsm>() 2648 * .implClass(MyBean.<jk>class</jk>, MyBeanImpl.<jk>class</jk>) 2649 * .build(); 2650 * 2651 * <jc>// Instantiates a MyBeanImpl,</jc> 2652 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"..."</js>, MyBean.<jk>class</jk>); 2653 * </p> 2654 * 2655 * @param interfaceClass The interface class. 2656 * <br>Cannot be <jk>null</jk>. 2657 * @param implClass The implementation class. 2658 * <br>Cannot be <jk>null</jk>. 2659 * @return This object. 2660 */ 2661 public Builder implClass(Class<?> interfaceClass, Class<?> implClass) { 2662 reqnn("interfaceClass", interfaceClass); 2663 reqnn("implClass", implClass); 2664 return annotations(MarshalledApplyAnnotation.create(interfaceClass).value(MarshalledAnnotation.create().implClass(implClass).build()).build()); 2665 } 2666 2667 /** 2668 * Implementation classes. 2669 * 2670 * <p> 2671 * For interfaces and abstract classes this method can be used to specify an implementation class for the 2672 * interface/abstract class so that instances of the implementation class are used when instantiated (e.g. during a 2673 * parse). 2674 * 2675 * <h5 class='section'>Example:</h5> 2676 * <p class='bjava'> 2677 * <jc>// A bean with a single property.</jc> 2678 * <jk>public interface</jk> MyBean { 2679 * ... 2680 * } 2681 * 2682 * <jc>// A bean with a single property.</jc> 2683 * <jk>public class</jk> MyBeanImpl <jk>implements</jk> MyBean { 2684 * ... 2685 * } 2686 2687 * <jc>// Create a parser that instantiates MyBeanImpls when parsing MyBeans.</jc> 2688 * ReaderParser <jv>parser</jv> = JsonParser 2689 * .<jsm>create</jsm>() 2690 * .implClasses(AMap.<jsm>of</jsm>(MyBean.<jk>class</jk>, MyBeanImpl.<jk>class</jk>)) 2691 * .build(); 2692 * 2693 * <jc>// Instantiates a MyBeanImpl,</jc> 2694 * MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<js>"..."</js>, MyBean.<jk>class</jk>); 2695 * </p> 2696 * 2697 * @param values 2698 * The new value for this setting. 2699 * <br>Cannot be <jk>null</jk>. 2700 * @return This object. 2701 */ 2702 public Builder implClasses(Map<Class<?>,Class<?>> values) { 2703 reqnn("values", values); 2704 values.forEach((k, v) -> annotations(MarshalledApplyAnnotation.create(k).value(MarshalledAnnotation.create().implClass(v).build()).build())); 2705 return this; 2706 } 2707 2708 /** 2709 * Identifies a class to be used as the interface class for the specified class and all subclasses. 2710 * 2711 * <p> 2712 * When specified, only the list of properties defined on the interface class will be used during serialization. 2713 * Additional properties on subclasses will be ignored. 2714 * 2715 * <p class='bjava'> 2716 * <jc>// Parent class or interface</jc> 2717 * <jk>public abstract class</jk> A { 2718 * <jk>public</jk> String <jf>foo</jf> = <js>"foo"</js>; 2719 * } 2720 * 2721 * <jc>// Sub class</jc> 2722 * <jk>public class</jk> A1 <jk>extends</jk> A { 2723 * <jk>public</jk> String <jf>bar</jf> = <js>"bar"</js>; 2724 * } 2725 * 2726 * <jc>// Create a serializer and define our interface class mapping.</jc> 2727 * WriterSerializer <jv>serializer</jv> = JsonSerializer 2728 * .<jsm>create</jsm>() 2729 * .interfaceClass(A1.<jk>class</jk>, A.<jk>class</jk>) 2730 * .build(); 2731 * 2732 * <jc>// Produces "{"foo":"foo"}"</jc> 2733 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> A1()); 2734 * </p> 2735 * 2736 * <p> 2737 * This annotation can be used on the parent class so that it filters to all child classes, or can be set 2738 * individually on the child classes. 2739 * 2740 * <h5 class='section'>Notes:</h5><ul> 2741 * <li class='note'>The {@link BeanType#interfaceClass() @BeanType(interfaceClass)} annotation is the equivalent annotation-based solution. 2742 * </ul> 2743 * 2744 * @param on The class that the interface class applies to. 2745 * <br>Cannot be <jk>null</jk>. 2746 * @param value 2747 * The new value for this setting. 2748 * <br>Cannot be <jk>null</jk>. 2749 * @return This object. 2750 */ 2751 public Builder interfaceClass(Class<?> on, Class<?> value) { 2752 reqnn("on", on); 2753 reqnn("value", value); 2754 return annotations(BeanTypeApplyAnnotation.create(on).value(BeanTypeAnnotation.create().interfaceClass(value).build()).build()); 2755 } 2756 2757 /** 2758 * Identifies a set of interfaces. 2759 * 2760 * <p> 2761 * When specified, only the list of properties defined on the interface class will be used during serialization 2762 * of implementation classes. Additional properties on subclasses will be ignored. 2763 * 2764 * <p class='bjava'> 2765 * <jc>// Parent class or interface</jc> 2766 * <jk>public abstract class</jk> A { 2767 * <jk>public</jk> String <jf>foo</jf> = <js>"foo"</js>; 2768 * } 2769 * 2770 * <jc>// Sub class</jc> 2771 * <jk>public class</jk> A1 <jk>extends</jk> A { 2772 * <jk>public</jk> String <jf>bar</jf> = <js>"bar"</js>; 2773 * } 2774 * 2775 * <jc>// Create a serializer and define our interface class mapping.</jc> 2776 * WriterSerializer <jv>serializer</jv> = JsonSerializer 2777 * .<jsm>create</jsm>() 2778 * .interfaces(A.<jk>class</jk>) 2779 * .build(); 2780 * 2781 * <jc>// Produces "{"foo":"foo"}"</jc> 2782 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> A1()); 2783 * </p> 2784 * 2785 * <p> 2786 * This annotation can be used on the parent class so that it filters to all child classes, or can be set 2787 * individually on the child classes. 2788 * 2789 * <h5 class='section'>Notes:</h5><ul> 2790 * <li class='note'>The {@link BeanType#interfaceClass() @BeanType(interfaceClass)} annotation is the equivalent annotation-based solution. 2791 * </ul> 2792 * 2793 * @param value 2794 * The new value for this setting. 2795 * <br>Cannot be <jk>null</jk>. 2796 * @return This object. 2797 */ 2798 public Builder interfaces(Class<?>...value) { 2799 reqnns("value", value); 2800 for (var v : value) 2801 annotations(BeanTypeApplyAnnotation.create(v).value(BeanTypeAnnotation.create().interfaceClass(v).build()).build()); 2802 return this; 2803 } 2804 2805 /** 2806 * <i><l>Context</l> configuration property: </i> Locale. 2807 * 2808 * <p> 2809 * Specifies the default locale for serializer and parser sessions when not specified via {@link MarshallingSession.Builder#locale(Locale)}. 2810 * Typically used for POJO swaps that need to deal with locales such as swaps that convert <l>Date</l> and <l>Calendar</l> 2811 * objects to strings by accessing it via the session passed into the {@link ObjectSwap#swap(MarshallingSession, Object)} and 2812 * {@link ObjectSwap#unswap(MarshallingSession, Object, ClassMeta, String)} methods. 2813 * 2814 * <h5 class='section'>Example:</h5> 2815 * <p class='bjava'> 2816 * <jc>// Define a POJO swap that skips serializing beans if we're in the UK.</jc> 2817 * <jk>public class</jk> MyBeanSwap <jk>extends</jk> StringSwap<MyBean> { 2818 * <ja>@Override</ja> 2819 * <jk>public</jk> String swap(MarshallingSession <jv>session</jv>, MyBean <jv>bean</jv>) <jk>throws</jk> Exception { 2820 * <jk>if</jk> (<jv>session</jv>.getLocale().equals(Locale.<jsf>UK</jsf>)) 2821 * <jk>return null</jk>; 2822 * <jk>return</jk> <jv>bean</jv>.toString(); 2823 * } 2824 * } 2825 * 2826 * <jc>// Create a serializer that uses the specified locale if it's not passed in through session args.</jc> 2827 * WriterSerializer <jv>serializer</jv> = JsonSerializer 2828 * .<jsm>create</jsm>() 2829 * .locale(Locale.<jsf>UK</jsf>) 2830 * .swaps(MyBeanSwap.<jk>class</jk>) 2831 * .build(); 2832 * </p> 2833 * 2834 * <h5 class='section'>See Also:</h5><ul> 2835 * <li class='ja'>{@link MarshalledConfig#locale()} 2836 * <li class='jm'>{@link MarshallingSession.Builder#locale(Locale)} 2837 * </ul> 2838 * 2839 * @param value The new value for this property. 2840 * <br>Cannot be <jk>null</jk>. 2841 * @return This object. 2842 */ 2843 public Builder locale(Locale value) { 2844 locale = reqnn("value", value); 2845 return this; 2846 } 2847 2848 /** 2849 * <i><l>Context</l> configuration property: </i> Media type. 2850 * 2851 * <p> 2852 * Specifies the default media type for serializer and parser sessions when not specified via {@link MarshallingSession.Builder#mediaType(MediaType)}. 2853 * Typically used for POJO swaps that need to serialize the same POJO classes differently depending on 2854 * the specific requested media type. For example, a swap could handle a request for media types <js>"application/json"</js> 2855 * and <js>"application/json+foo"</js> slightly differently even though they're both being handled by the same JSON 2856 * serializer or parser. 2857 * 2858 * <h5 class='section'>Example:</h5> 2859 * <p class='bjava'> 2860 * <jc>// Define a POJO swap that skips serializing beans if the media type is application/json.</jc> 2861 * <jk>public class</jk> MyBeanSwap <jk>extends</jk> StringSwap<MyBean> { 2862 * <ja>@Override</ja> 2863 * <jk>public</jk> String swap(MarshallingSession <jv>session</jv>, MyBean <jv>bean</jv>) <jk>throws</jk> Exception { 2864 * <jk>if</jk> (<jv>session</jv>.getMediaType().equals(<js>"application/json"</js>)) 2865 * <jk>return null</jk>; 2866 * <jk>return</jk> <jv>bean</jv>.toString(); 2867 * } 2868 * } 2869 * 2870 * <jc>// Create a serializer that uses the specified media type if it's not passed in through session args.</jc> 2871 * WriterSerializer <jv>serializer</jv> = JsonSerializer 2872 * .<jsm>create</jsm>() 2873 * .mediaType(MediaType.<jsf>JSON</jsf>) 2874 * .build(); 2875 * </p> 2876 * 2877 * <h5 class='section'>See Also:</h5><ul> 2878 * <li class='ja'>{@link MarshalledConfig#mediaType()} 2879 * <li class='jm'>{@link MarshallingSession.Builder#mediaType(MediaType)} 2880 * </ul> 2881 * 2882 * @param value The new value for this property. 2883 * <br>Can be <jk>null</jk> (no default media type will be set). 2884 * @return This object. 2885 */ 2886 public Builder mediaType(MediaType value) { 2887 mediaType = value; 2888 return this; 2889 } 2890 2891 /** 2892 * Returns the set of not-bean classes. 2893 * 2894 * <p> 2895 * Gives access to the inner set if you need to make more than simple additions via {@link #notBeanClasses(ClassInfo...)}. 2896 * 2897 * @return The set of not-bean classes. 2898 * @see #notBeanClasses(ClassInfo...) 2899 */ 2900 public Set<ClassInfo> notBeanClasses() { 2901 return notBeanClasses; 2902 } 2903 2904 /** 2905 * Convenience method for {@link #notBeanClasses(ClassInfo...)} that accepts {@link Class} objects. 2906 * 2907 * @param values 2908 * The values to add to this setting. 2909 * <br>Cannot contain <jk>null</jk> values. 2910 * @return This object. 2911 * @see #notBeanClasses(ClassInfo...) 2912 */ 2913 public Builder notBeanClasses(Class<?>...values) { 2914 reqnns("values", values); 2915 return notBeanClasses(Stream.of(values).map(x -> info(x)).toArray(ClassInfo[]::new)); 2916 } 2917 2918 /** 2919 * Bean class exclusions. 2920 * 2921 * <p> 2922 * List of classes that should not be treated as beans even if they appear to be bean-like. 2923 * Not-bean classes are converted to <c>Strings</c> during serialization. 2924 * 2925 * <p> 2926 * Values can consist of any of the following types: 2927 * <ul> 2928 * <li>Classes. 2929 * <li>Arrays and collections of classes. 2930 * </ul> 2931 * 2932 * <h5 class='section'>Example:</h5> 2933 * <p class='bjava'> 2934 * <jc>// A bean with a single property.</jc> 2935 * <jk>public class</jk> MyBean { 2936 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 2937 * 2938 * <jk>public</jk> String toString() { 2939 * <jk>return</jk> <js>"baz"</js>; 2940 * } 2941 * } 2942 * 2943 * <jc>// Create a serializer that doesn't treat MyBean as a bean class.</jc> 2944 * WriterSerializer <jv>serializer</jv> = JsonSerializer 2945 * .<jsm>create</jsm>() 2946 * .notBeanClasses(MyBean.<jk>class</jk>) 2947 * .build(); 2948 * 2949 * <jc>// Produces "baz" instead of {"foo":"bar"}</jc> 2950 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 2951 * </p> 2952 * 2953 * <h5 class='section'>Notes:</h5><ul> 2954 * <li class='note'>The {@link BeanIgnore @BeanIgnore} annotation can also be used on classes to prevent them from being recognized as beans. 2955 * </ul> 2956 * 2957 * <h5 class='section'>See Also:</h5><ul> 2958 * <li class='ja'>{@link BeanIgnore} 2959 * <li class='ja'>{@link BeanConfig#notBeanClasses()} 2960 * </ul> 2961 * 2962 * @param values 2963 * The values to add to this setting. 2964 * <br>Cannot contain <jk>null</jk> values. 2965 * @return This object. 2966 */ 2967 public Builder notBeanClasses(ClassInfo...values) { 2968 reqnns("values", values); 2969 notBeanClasses().addAll(l(values)); 2970 return this; 2971 } 2972 2973 /** 2974 * Same as {@link #notBeanClasses(ClassInfo...)} but allows you to pass in a collection of class info objects. 2975 * 2976 * @param values 2977 * The values to add to this setting. 2978 * <br>Cannot be <jk>null</jk> or contain <jk>null</jk> values. 2979 * @return This object. 2980 * @see #notBeanClasses(ClassInfo...) 2981 */ 2982 public Builder notBeanClasses(Collection<ClassInfo> values) { 2983 reqnns("values", values); 2984 notBeanClasses().addAll(values); 2985 return this; 2986 } 2987 2988 /** 2989 * Returns the list of not-bean Java package names. 2990 * 2991 * <p> 2992 * Gives access to the inner list if you need to make more than simple additions via {@link #notBeanPackages(String...)}. 2993 * 2994 * @return The list of not-bean Java package names. 2995 * @see #notBeanPackages(String...) 2996 */ 2997 public Set<String> notBeanPackages() { 2998 return notBeanPackages; 2999 } 3000 3001 /** 3002 * Same as {@link #notBeanPackages(String...)} but allows you to pass in a collection of classes. 3003 * 3004 * @param values 3005 * The values to add to this setting. 3006 * <br>Cannot be <jk>null</jk> or contain <jk>null</jk> values. 3007 * @return This object. 3008 * @see #notBeanPackages(String...) 3009 */ 3010 public Builder notBeanPackages(Collection<String> values) { 3011 reqnns("values", values); 3012 notBeanPackages().addAll(values); 3013 return this; 3014 } 3015 3016 /** 3017 * Bean package exclusions. 3018 * 3019 * <p> 3020 * Used as a convenient way of defining the {@link #notBeanClasses(Class...)} property for entire packages. 3021 * Any classes within these packages will be serialized to strings using {@link Object#toString()}. 3022 * 3023 * <p> 3024 * Note that you can specify suffix patterns to include all subpackages. 3025 * 3026 * <p> 3027 * Values can consist of any of the following types: 3028 * <ul> 3029 * <li>Strings. 3030 * <li>Arrays and collections of strings. 3031 * </ul> 3032 * 3033 * <h5 class='section'>Example:</h5> 3034 * <p class='bjava'> 3035 * <jc>// Create a serializer that ignores beans in the specified packages.</jc> 3036 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3037 * .<jsm>create</jsm>() 3038 * .notBeanPackages(<js>"org.apache.foo"</js>, <js>"org.apache.bar.*"</js>) 3039 * .build(); 3040 * </p> 3041 * 3042 * @param values 3043 * The values to add to this setting. 3044 * <br>Values can consist of any of the following types: 3045 * <ul> 3046 * <li>{@link Package} objects. 3047 * <li>Strings. 3048 * <li>Arrays and collections of anything in this list. 3049 * </ul> 3050 * <br>Cannot contain <jk>null</jk> values. 3051 * @return This object. 3052 */ 3053 public Builder notBeanPackages(String...values) { 3054 reqnns("values", values); 3055 return notBeanPackages(l(values)); 3056 } 3057 3058 /** 3059 * Bean property namer 3060 * 3061 * <p> 3062 * Same as {@link #propertyNamer(Class)} but allows you to specify a namer for a specific class. 3063 * 3064 * <h5 class='section'>Example:</h5> 3065 * <p class='bjava'> 3066 * <jc>// A bean with a single property.</jc> 3067 * <jk>public class</jk> MyBean { 3068 * <jk>public</jk> String <jf>fooBarBaz</jf> = <js>"fooBarBaz"</js>; 3069 * } 3070 * 3071 * <jc>// Create a serializer that uses Dashed-Lower-Case property names for the MyBean class only.</jc> 3072 * <jc>// (e.g. "foo-bar-baz" instead of "fooBarBaz")</jc> 3073 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3074 * .<jsm>create</jsm>() 3075 * .propertyNamer(MyBean.<jk>class</jk>, PropertyNamerDLC.<jk>class</jk>) 3076 * .build(); 3077 * 3078 * <jc>// Produces: {"foo-bar-baz":"fooBarBaz"}</jc> 3079 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 3080 * </p> 3081 * 3082 * <h5 class='section'>See Also:</h5><ul> 3083 * <li class='ja'>{@link BeanType#propertyNamer() @BeanType(propertyNamer)} 3084 * <li class='jm'>{@link #propertyNamer(Class)} 3085 * </ul> 3086 * 3087 * @param on The class that the namer applies to. 3088 * <br>Cannot be <jk>null</jk>. 3089 * @param value 3090 * The new value for this setting. 3091 * <br>The default is {@link BasicPropertyNamer}. 3092 * <br>Cannot be <jk>null</jk>. 3093 * @return This object. 3094 */ 3095 public Builder propertyNamer(Class<?> on, Class<? extends PropertyNamer> value) { 3096 reqnn("on", on); 3097 reqnn("value", value); 3098 return annotations(BeanTypeApplyAnnotation.create(on).value(BeanTypeAnnotation.create().propertyNamer(value).build()).build()); 3099 } 3100 3101 /** 3102 * Bean property namer 3103 * 3104 * <p> 3105 * The class to use for calculating bean property names. 3106 * 3107 * <p> 3108 * Predefined classes: 3109 * <ul> 3110 * <li>{@link BasicPropertyNamer} - Default. 3111 * <li>{@link PropertyNamerDLC} - Dashed-lower-case names. 3112 * <li>{@link PropertyNamerULC} - Dashed-upper-case names. 3113 * </ul> 3114 * 3115 * <h5 class='section'>Example:</h5> 3116 * <p class='bjava'> 3117 * <jc>// A bean with a single property.</jc> 3118 * <jk>public class</jk> MyBean { 3119 * <jk>public</jk> String <jf>fooBarBaz</jf> = <js>"fooBarBaz"</js>; 3120 * } 3121 * 3122 * <jc>// Create a serializer that uses Dashed-Lower-Case property names.</jc> 3123 * <jc>// (e.g. "foo-bar-baz" instead of "fooBarBaz")</jc> 3124 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3125 * .<jsm>create</jsm>() 3126 * .propertyNamer(PropertyNamerDLC.<jk>class</jk>) 3127 * .build(); 3128 * 3129 * <jc>// Produces: {"foo-bar-baz":"fooBarBaz"}</jc> 3130 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 3131 * </p> 3132 * 3133 * @param value 3134 * The new value for this setting. 3135 * <br>Can be <jk>null</jk> (will use {@link BasicPropertyNamer} as the default). 3136 * @return This object. 3137 */ 3138 public Builder propertyNamer(Class<? extends PropertyNamer> value) { 3139 propertyNamer = value; 3140 return this; 3141 } 3142 3143 /** 3144 * Disable sorted bean properties. 3145 * 3146 * <p> 3147 * By default, all bean properties are serialized and accessed in alphabetical order. 3148 * Calling this method opts out of that behavior so that declaration order is used instead: record component order for records, field declaration order (superclass first) for other classes, then method-only properties alphabetically. 3149 * 3150 * <h5 class='section'>Example:</h5> 3151 * <p class='bjava'> 3152 * <jc>// A bean with 3 properties.</jc> 3153 * <jk>public class</jk> MyBean { 3154 * <jk>public</jk> String <jf>c</jf> = <js>"1"</js>; 3155 * <jk>public</jk> String <jf>b</jf> = <js>"2"</js>; 3156 * <jk>public</jk> String <jf>a</jf> = <js>"3"</js>; 3157 * } 3158 * 3159 * <jc>// Create a serializer that does NOT sort bean properties.</jc> 3160 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3161 * .<jsm>create</jsm>() 3162 * .unsortedProperties() 3163 * .build(); 3164 * 3165 * <jc>// Produces: {"c":"1","b":"2","a":"3"} (declaration order)</jc> 3166 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 3167 * </p> 3168 * 3169 * <h5 class='section'>Notes:</h5><ul> 3170 * <li class='note'>The {@link BeanType#unsorted() @BeanType.unsorted()} annotation can also be used to opt out of sorting on a single class. 3171 * </ul> 3172 * 3173 * @return This object. 3174 */ 3175 public Builder unsortedProperties() { 3176 return unsortedProperties(true); 3177 } 3178 3179 /** 3180 * Same as {@link #unsortedProperties()} but allows you to explicitly specify the value. 3181 * 3182 * @param value The value for this setting. 3183 * @return This object. 3184 */ 3185 public Builder unsortedProperties(boolean value) { 3186 unsortedProperties = value; 3187 return this; 3188 } 3189 3190 /** 3191 * Use record component order for {@link Record} beans. 3192 * 3193 * <p> 3194 * By default, record properties are sorted alphabetically like every other bean. Calling this method makes 3195 * records keep their component (declaration) order, even when other beans are sorted. Non-record beans are 3196 * unaffected. 3197 * 3198 * <h5 class='section'>Example:</h5> 3199 * <p class='bjava'> 3200 * <jk>public record</jk> Person(String <jv>name</jv>, <jk>int</jk> <jv>age</jv>) {} 3201 * 3202 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3203 * .<jsm>create</jsm>() 3204 * .recordComponentOrder() 3205 * .build(); 3206 * 3207 * <jc>// Produces: {"name":"Alice","age":30}</jc> 3208 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> Person(<js>"Alice"</js>, 30)); 3209 * </p> 3210 * 3211 * <h5 class='section'>See Also:</h5><ul> 3212 * <li class='jm'>{@link #unsortedProperties()} (declaration order for every bean, records included) 3213 * <li class='ja'>{@link BeanConfig#recordComponentOrder()} 3214 * </ul> 3215 * 3216 * @return This object. 3217 */ 3218 public Builder recordComponentOrder() { 3219 return recordComponentOrder(true); 3220 } 3221 3222 /** 3223 * Same as {@link #recordComponentOrder()} but allows you to explicitly specify the value. 3224 * 3225 * @param value The value for this setting. 3226 * @return This object. 3227 */ 3228 public Builder recordComponentOrder(boolean value) { 3229 recordComponentOrder = value; 3230 return this; 3231 } 3232 3233 /** 3234 * Opt specific bean classes out of alphabetical property sorting. 3235 * 3236 * <p> 3237 * Applies the {@link BeanType#unsorted() @BeanType(unsorted=true)} annotation to the specified classes, 3238 * overriding the default sorted behavior for those specific beans. 3239 * 3240 * <h5 class='section'>See Also:</h5><ul> 3241 * <li class='ja'>{@link BeanType#unsorted() @BeanType(unsorted)} 3242 * </ul> 3243 * 3244 * @param on The bean classes to opt out of sorted properties. 3245 * <br>Cannot contain <jk>null</jk> values. 3246 * @return This object. 3247 */ 3248 public Builder unsortedProperties(Class<?>...on) { 3249 reqnns("on", on); 3250 for (var c : on) 3251 annotations(BeanTypeApplyAnnotation.create(c).value(BeanTypeAnnotation.create().unsorted(true).build()).build()); 3252 return this; 3253 } 3254 3255 /** 3256 * Identifies a stop class for the annotated class. 3257 * 3258 * <p> 3259 * Identical in purpose to the stop class specified by {@link Introspector#getBeanInfo(Class, Class)}. 3260 * Any properties in the stop class or in its base classes will be ignored during analysis. 3261 * 3262 * <p> 3263 * For example, in the following class hierarchy, instances of <c>C3</c> will include property <c>p3</c>, 3264 * but not <c>p1</c> or <c>p2</c>. 3265 * 3266 * <h5 class='section'>Example:</h5> 3267 * <p class='bjava'> 3268 * <jk>public class</jk> C1 { 3269 * <jk>public int</jk> getP1(); 3270 * } 3271 * 3272 * <jk>public class</jk> C2 <jk>extends</jk> C1 { 3273 * <jk>public int</jk> getP2(); 3274 * } 3275 * 3276 * <jk>public class</jk> C3 <jk>extends</jk> C2 { 3277 * <jk>public int</jk> getP3(); 3278 * } 3279 * 3280 * <jc>// Create a serializer specifies a stop class for C3.</jc> 3281 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3282 * .<jsm>create</jsm>() 3283 * .stopClass(C3.<jk>class</jk>, C2.<jk>class</jk>) 3284 * .build(); 3285 * 3286 * <jc>// Produces: {"p3":"..."}</jc> 3287 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> C3()); 3288 * </p> 3289 * 3290 * @param on The class on which the stop class is being applied. 3291 * <br>Cannot be <jk>null</jk>. 3292 * @param value 3293 * The new value for this setting. 3294 * <br>Cannot be <jk>null</jk>. 3295 * @return This object. 3296 */ 3297 public Builder stopClass(Class<?> on, Class<?> value) { 3298 reqnn("on", on); 3299 reqnn("value", value); 3300 return annotations(BeanTypeApplyAnnotation.create(on).value(BeanTypeAnnotation.create().stopClass(value).build()).build()); 3301 } 3302 3303 /** 3304 * A shortcut for defining a {@link FunctionalSwap}. 3305 * 3306 * <h5 class='section'>Example:</h5> 3307 * <p class='bjava'> 3308 * <jc>// Create a serializer that performs a custom format for DAte objects.</jc> 3309 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3310 * .<jsm>create</jsm>() 3311 * .swap(Date.<jk>class</jk>, String.<jk>class</jk>, <jv>x</jv> -> <jsm>format</jsm>(<jv>x</jv>)) 3312 * .build(); 3313 * </p> 3314 * 3315 * @param <T> The object type being swapped out. 3316 * @param <S> The object type being swapped in. 3317 * @param normalClass The object type being swapped out. 3318 * <br>Cannot be <jk>null</jk>. 3319 * @param swappedClass The object type being swapped in. 3320 * <br>Cannot be <jk>null</jk>. 3321 * @param swapFunction The function to convert the object. 3322 * <br>Cannot be <jk>null</jk>. 3323 * @return This object. 3324 */ 3325 public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction) { 3326 reqnn("normalClass", normalClass); 3327 reqnn("swappedClass", swappedClass); 3328 reqnn("swapFunction", swapFunction); 3329 swaps().add(0, new FunctionalSwap<>(normalClass, swappedClass, swapFunction, null)); 3330 return this; 3331 } 3332 3333 /** 3334 * A shortcut for defining a {@link FunctionalSwap}. 3335 * 3336 * <h5 class='section'>Example:</h5> 3337 * <p class='bjava'> 3338 * <jc>// Create a serializer that performs a custom format for Date objects.</jc> 3339 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3340 * .<jsm>create</jsm>() 3341 * .swap(Date.<jk>class</jk>, String.<jk>class</jk>, <jv>x</jv> -> <jsm>format</jsm>(<jv>x</jv>), <jv>x</jv> -> <jsm>parse</jsm>(<jv>x</jv>)) 3342 * .build(); 3343 * </p> 3344 * 3345 * @param <T> The object type being swapped out. 3346 * @param <S> The object type being swapped in. 3347 * @param normalClass The object type being swapped out. 3348 * <br>Cannot be <jk>null</jk>. 3349 * @param swappedClass The object type being swapped in. 3350 * <br>Cannot be <jk>null</jk>. 3351 * @param swapFunction The function to convert the object during serialization. 3352 * <br>Cannot be <jk>null</jk>. 3353 * @param unswapFunction The function to convert the object during parsing. 3354 * <br>Cannot be <jk>null</jk>. 3355 * @return This object. 3356 */ 3357 public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction, ThrowingFunction<S,T> unswapFunction) { 3358 reqnn("normalClass", normalClass); 3359 reqnn("swappedClass", swappedClass); 3360 reqnn("swapFunction", swapFunction); 3361 reqnn("unswapFunction", unswapFunction); 3362 swaps().add(0, new FunctionalSwap<>(normalClass, swappedClass, swapFunction, unswapFunction)); 3363 return this; 3364 } 3365 3366 /** 3367 * Returns the bean swaps list. 3368 * 3369 * <p> 3370 * Gives access to the inner list if you need to make more than simple additions via {@link #swaps(Class...)}. 3371 * 3372 * @return The bean swaps list. 3373 * @see #swaps(Class...) 3374 */ 3375 public List<Object> swaps() { 3376 return swaps; 3377 } 3378 3379 /** 3380 * Same as {@link #swaps(Object...)} but explicitly specifies an array of classes to avoid compilation warnings. 3381 * 3382 * @param values 3383 * The values to add to this setting. 3384 * <br>Values can consist of any of the following types: 3385 * <ul> 3386 * <li>Any subclass of {@link ObjectSwap}. 3387 * <li>Any surrogate class. A shortcut for defining a {@link SurrogateSwap}. 3388 * </ul> 3389 * <br>Cannot be <jk>null</jk>. 3390 * @return This object. 3391 */ 3392 public Builder swaps(Class<?>...values) { 3393 reqnns("values", values); 3394 swaps().addAll(0, accumulate((Object[])values)); 3395 return this; 3396 } 3397 3398 /** 3399 * Java object swaps. 3400 * 3401 * <p> 3402 * Swaps are used to "swap out" non-serializable classes with serializable equivalents during serialization, 3403 * and "swap in" the non-serializable class during parsing. 3404 * 3405 * <p> 3406 * An example of a swap would be a <c>Calendar</c> object that gets swapped out for an ISO8601 string. 3407 * 3408 * <p> 3409 * Multiple swaps can be associated with a single class. 3410 * When multiple swaps are applicable to the same class, the media type pattern defined by 3411 * {@link ObjectSwap#forMediaTypes()} or {@link Swap#mediaTypes() @Swap(mediaTypes)} are used to come up with the best match. 3412 * 3413 * <p> 3414 * Values can consist of any of the following types: 3415 * <ul> 3416 * <li>Any subclass of {@link ObjectSwap}. 3417 * <li>Any instance of {@link ObjectSwap}. 3418 * <li>Any surrogate class. A shortcut for defining a {@link SurrogateSwap}. 3419 * <li>Any array or collection of the objects above. 3420 * </ul> 3421 * 3422 * <h5 class='section'>Example:</h5> 3423 * <p class='bjava'> 3424 * <jc>// Sample swap for converting Dates to ISO8601 strings.</jc> 3425 * <jk>public class</jk> MyDateSwap <jk>extends</jk> StringSwap<Date> { 3426 * <jc>// ISO8601 formatter.</jc> 3427 * <jk>private</jk> DateFormat <jf>format</jf> = <jk>new</jk> SimpleDateFormat(<js>"yyyy-MM-dd'T'HH:mm:ssZ"</js>); 3428 * 3429 * <ja>@Override</ja> 3430 * <jk>public</jk> String swap(MarshallingSession <jv>session</jv>, Date <jv>date</jv>) { 3431 * <jk>return</jk> <jf>format</jf>.format(<jv>date</jv>); 3432 * } 3433 * 3434 * <ja>@Override</ja> 3435 * <jk>public</jk> Date unswap(MarshallingSession <jv>session</jv>, String <jv>string</jv>, ClassMeta <jv>hint</jv>) <jk>throws</jk> Exception { 3436 * <jk>return</jk> <jf>format</jf>.parse(<jv>string</jv>); 3437 * } 3438 * } 3439 * 3440 * <jc>// Sample bean with a Date field.</jc> 3441 * <jk>public class</jk> MyBean { 3442 * <jk>public</jk> Date <jf>date</jf> = <jk>new</jk> Date(112, 2, 3, 4, 5, 6); 3443 * } 3444 * 3445 * <jc>// Create a serializer that uses our date swap.</jc> 3446 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3447 * .<jsm>create</jsm>() 3448 * .swaps(MyDateSwap.<jk>class</jk>) 3449 * .build(); 3450 * 3451 * <jc>// Produces: {"date":"2012-03-03T04:05:06-0500"}</jc> 3452 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 3453 * 3454 * <jc>// Create a serializer that uses our date swap.</jc> 3455 * ReaderParser <jv>parser</jv> = JsonParser 3456 * .<jsm>create</jsm>() 3457 * .swaps(MyDateSwap.<jk>class</jk>) 3458 * .build(); 3459 * 3460 * <jc>// Use our parser to parse a bean.</jc> 3461 * MyBean <jv>bean</jv> = <jv>parser</jv>.read(<jv>json</jv>, MyBean.<jk>class</jk>); 3462 * </p> 3463 * 3464 * <h5 class='section'>Notes:</h5><ul> 3465 * <li class='note'>The {@link Swap @Swap} annotation can also be used on classes to identify swaps for the class. 3466 * <li class='note'>The {@link Swap @Swap} annotation can also be used on bean methods and fields to identify swaps for values of those bean properties. 3467 * </ul> 3468 * 3469 * @param values 3470 * The values to add to this setting. 3471 * <br>Values can consist of any of the following types: 3472 * <ul> 3473 * <li>Any subclass of {@link ObjectSwap}. 3474 * <li>Any surrogate class. A shortcut for defining a {@link SurrogateSwap}. 3475 * <li>Any array/collection/stream of the objects above. 3476 * </ul> 3477 * <br>Cannot be <jk>null</jk>. 3478 * @return This object. 3479 */ 3480 public Builder swaps(Object...values) { 3481 reqnns("values", values); 3482 swaps().addAll(0, accumulate(values)); 3483 return this; 3484 } 3485 3486 /** 3487 * <i><l>Context</l> configuration property: </i> TimeZone. 3488 * 3489 * <p> 3490 * Specifies the default time zone for serializer and parser sessions when not specified via {@link MarshallingSession.Builder#timeZone(TimeZone)}. 3491 * Typically used for POJO swaps that need to deal with timezones such as swaps that convert <l>Date</l> and <l>Calendar</l> 3492 * objects to strings by accessing it via the session passed into the {@link ObjectSwap#swap(MarshallingSession, Object)} and 3493 * {@link ObjectSwap#unswap(MarshallingSession, Object, ClassMeta, String)} methods. 3494 * 3495 * <h5 class='section'>Example:</h5> 3496 * <p class='bjava'> 3497 * <jc>// Define a POJO swap that skips serializing beans if the time zone is GMT.</jc> 3498 * <jk>public class</jk> MyBeanSwap <jk>extends</jk> StringSwap<MyBean> { 3499 * <ja>@Override</ja> 3500 * <jk>public</jk> String swap(MarshallingSession <jv>session</jv>, MyBean <jv>bean</jv>) <jk>throws</jk> Exception { 3501 * <jk>if</jk> (<jv>session</jv>.getTimeZone().equals(TimeZone.<jsf>GMT</jsf>)) 3502 * <jk>return null</jk>; 3503 * <jk>return</jk> <jv>bean</jv>.toString(); 3504 * } 3505 * } 3506 * 3507 * <jc>// Create a serializer that uses GMT if the timezone is not specified in the session args.</jc> 3508 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3509 * .<jsm>create</jsm>() 3510 * .timeZone(TimeZone.<jsf>GMT</jsf>) 3511 * .build(); 3512 * </p> 3513 * 3514 * <h5 class='section'>See Also:</h5><ul> 3515 * <li class='ja'>{@link MarshalledConfig#timeZone()} 3516 * <li class='jm'>{@link MarshallingSession.Builder#timeZone(TimeZone)} 3517 * </ul> 3518 * 3519 * @param value The new value for this property. 3520 * <br>Can be <jk>null</jk> (timezone will not be set, defaults to system timezone). 3521 * @return This object. 3522 */ 3523 public Builder timeZone(TimeZone value) { 3524 timeZone = value; 3525 return this; 3526 } 3527 3528 /** 3529 * Duration wire format. 3530 * 3531 * @param value The value for this setting. 3532 * <br>Can be <jk>null</jk> (defaults to {@link DurationFormat#ISO_8601_WITH_DAYS}). 3533 * @return This object. 3534 */ 3535 public Builder durationFormat(DurationFormat value) { 3536 durationFormat = value == null ? DurationFormat.ISO_8601_WITH_DAYS : value; 3537 return this; 3538 } 3539 3540 /** 3541 * Period wire format. 3542 * 3543 * @param value The value for this setting. 3544 * <br>Can be <jk>null</jk> (defaults to {@link PeriodFormat#ISO_8601}). 3545 * @return This object. 3546 */ 3547 public Builder periodFormat(PeriodFormat value) { 3548 periodFormat = value == null ? PeriodFormat.ISO_8601 : value; 3549 return this; 3550 } 3551 3552 /** 3553 * Calendar wire format. 3554 * 3555 * @param value The value for this setting. 3556 * <br>Can be <jk>null</jk> (defaults to {@link CalendarFormat#ISO_OFFSET_DATE_TIME}). 3557 * @return This object. 3558 */ 3559 public Builder calendarFormat(CalendarFormat value) { 3560 calendarFormat = value == null ? CalendarFormat.ISO_OFFSET_DATE_TIME : value; 3561 return this; 3562 } 3563 3564 /** 3565 * Date wire format. 3566 * 3567 * @param value The value for this setting. 3568 * <br>Can be <jk>null</jk> (defaults to {@link DateFormat#ISO_LOCAL_DATE_TIME}). 3569 * @return This object. 3570 */ 3571 public Builder dateFormat(DateFormat value) { 3572 dateFormat = value == null ? DateFormat.ISO_LOCAL_DATE_TIME : value; 3573 return this; 3574 } 3575 3576 /** 3577 * Temporal wire format. 3578 * 3579 * @param value The value for this setting. 3580 * <br>Can be <jk>null</jk> (defaults to {@link TemporalFormat#DEFAULT}). 3581 * @return This object. 3582 */ 3583 public Builder temporalFormat(TemporalFormat value) { 3584 temporalFormat = value == null ? TemporalFormat.DEFAULT : value; 3585 return this; 3586 } 3587 3588 /** 3589 * Time-zone wire format. 3590 * 3591 * @param value The value for this setting. 3592 * <br>Can be <jk>null</jk> (defaults to {@link TimeZoneFormat#ID}). 3593 * @return This object. 3594 */ 3595 public Builder timeZoneFormat(TimeZoneFormat value) { 3596 timeZoneFormat = value == null ? TimeZoneFormat.ID : value; 3597 return this; 3598 } 3599 3600 /** 3601 * Locale wire format. 3602 * 3603 * @param value The value for this setting. 3604 * <br>Can be <jk>null</jk> (defaults to {@link LocaleFormat#BCP_47}). 3605 * @return This object. 3606 */ 3607 public Builder localeFormat(LocaleFormat value) { 3608 localeFormat = value == null ? LocaleFormat.BCP_47 : value; 3609 return this; 3610 } 3611 3612 /** 3613 * Binary wire format. 3614 * 3615 * <p> 3616 * Controls how <code><jk>byte</jk>[]</code> values are written to and read from text-based 3617 * serializer / parser wire formats, and the encoding used by stream-based serializers when 3618 * converting their byte output to a string. 3619 * 3620 * @param value The value for this setting. 3621 * <br>Can be <jk>null</jk> (defaults to {@link BinaryFormat#NOT_SET}). 3622 * @return This object. 3623 */ 3624 public Builder binaryFormat(BinaryFormat value) { 3625 binaryFormat = value == null ? BinaryFormat.NOT_SET : value; 3626 return this; 3627 } 3628 3629 /** 3630 * Enum wire format. 3631 * 3632 * @param value The value for this setting. 3633 * <br>Can be <jk>null</jk> (defaults to {@link EnumFormat#TO_STRING}). 3634 * @return This object. 3635 */ 3636 public Builder enumFormat(EnumFormat value) { 3637 enumFormat = value == null ? EnumFormat.TO_STRING : value; 3638 return this; 3639 } 3640 3641 /** 3642 * UUID wire format. 3643 * 3644 * <p> 3645 * Controls how {@link UUID} values are written to text-based wire formats. Binary 3646 * serializers (BSON / CBOR / MsgPack / Prototext) emit native 16-byte binary regardless of this setting. 3647 * 3648 * @param value The value for this setting. 3649 * <br>Can be <jk>null</jk> (defaults to {@link UuidFormat#STANDARD}). 3650 * @return This object. 3651 */ 3652 public Builder uuidFormat(UuidFormat value) { 3653 uuidFormat = value == null ? UuidFormat.STANDARD : value; 3654 return this; 3655 } 3656 3657 /** 3658 * BitSet wire format. 3659 * 3660 * <p> 3661 * Controls how {@link BitSet} values are written to text-based wire formats. 3662 * 3663 * @param value The value for this setting. 3664 * <br>Can be <jk>null</jk> (defaults to {@link BitSetFormat#INDICES}). 3665 * @return This object. 3666 */ 3667 public Builder bitSetFormat(BitSetFormat value) { 3668 bitSetFormat = value == null ? BitSetFormat.INDICES : value; 3669 return this; 3670 } 3671 3672 /** 3673 * Big-number wire format. 3674 * 3675 * <p> 3676 * Controls how {@link BigInteger} and {@link BigDecimal} values are written to 3677 * text-based wire formats. Useful for safe JS interop where values outside 3678 * {@code ±(2^53 − 1)} would silently lose precision. 3679 * 3680 * @param value The value for this setting. 3681 * <br>Can be <jk>null</jk> (defaults to {@link BigNumberFormat#NUMBER}). 3682 * @return This object. 3683 */ 3684 public Builder bigNumberFormat(BigNumberFormat value) { 3685 bigNumberFormat = value == null ? BigNumberFormat.NUMBER : value; 3686 return this; 3687 } 3688 3689 /** 3690 * Boolean wire format. 3691 * 3692 * <p> 3693 * Controls how {@link Boolean} / <code><jk>boolean</jk></code> values are written to text-based 3694 * wire formats. Binary serializers (BSON / CBOR / MsgPack / Prototext / Parquet) emit a native 3695 * boolean wire type regardless of this setting. 3696 * 3697 * @param value The value for this setting. 3698 * <br>Can be <jk>null</jk> (defaults to {@link BooleanFormat#TRUE_FALSE}). 3699 * @return This object. 3700 */ 3701 public Builder booleanFormat(BooleanFormat value) { 3702 booleanFormat = value == null ? BooleanFormat.TRUE_FALSE : value; 3703 return this; 3704 } 3705 3706 /** 3707 * Float / Double non-finite wire format. 3708 * 3709 * <p> 3710 * Controls how non-finite {@link Float} / {@link Double} values ({@code NaN} / {@code ±Infinity}) 3711 * are written to text-based wire formats. The default {@link FloatFormat#NaN_AS_NULL} is 3712 * JSON-spec-compliant. Binary serializers emit native IEEE-754 floats regardless of this setting. 3713 * 3714 * @param value The value for this setting. 3715 * <br>Can be <jk>null</jk> (defaults to {@link FloatFormat#NaN_AS_NULL}). 3716 * @return This object. 3717 */ 3718 public Builder floatFormat(FloatFormat value) { 3719 floatFormat = value == null ? FloatFormat.NaN_AS_NULL : value; 3720 return this; 3721 } 3722 3723 /** 3724 * Currency wire format. 3725 * 3726 * <p> 3727 * Controls how {@link Currency} values are written to text-based wire formats. The 3728 * default {@link CurrencyFormat#ISO_CODE} is the only setting safe for machine-to-machine wires; 3729 * {@link CurrencyFormat#SYMBOL} and {@link CurrencyFormat#DISPLAY_NAME} are locale-sensitive and 3730 * best-effort on the parse side. 3731 * 3732 * @param value The value for this setting. 3733 * <br>Can be <jk>null</jk> (defaults to {@link CurrencyFormat#ISO_CODE}). 3734 * @return This object. 3735 */ 3736 public Builder currencyFormat(CurrencyFormat value) { 3737 currencyFormat = value == null ? CurrencyFormat.ISO_CODE : value; 3738 return this; 3739 } 3740 3741 /** 3742 * Class wire format. 3743 * 3744 * <p> 3745 * Controls how {@link Class} values are written to text-based wire formats. The default 3746 * {@link ClassFormat#FQCN} matches the historical {@code ClassSwap} behavior. 3747 * {@link ClassFormat#SIMPLE_NAME} is serialize-only — the parser path throws 3748 * {@link UnsupportedOperationException}. 3749 * 3750 * @param value The value for this setting. 3751 * <br>Can be <jk>null</jk> (defaults to {@link ClassFormat#FQCN}). 3752 * @return This object. 3753 */ 3754 public Builder classFormat(ClassFormat value) { 3755 classFormat = value == null ? ClassFormat.FQCN : value; 3756 return this; 3757 } 3758 3759 /** 3760 * Session classloader. 3761 * 3762 * <p> 3763 * Sets the classloader used when parsing wire-format class names (e.g. FQCN or binary-name strings 3764 * written by {@link org.apache.juneau.marshall.swaps.ClassFormatSwap}). When <jk>null</jk>, the thread-context classloader is 3765 * used as a fallback — which is the historical behavior. 3766 * 3767 * <p> 3768 * Useful in OSGi bundles, webapp classloaders, and plugin systems where the thread-context 3769 * classloader does not have visibility into the classes that the session needs to resolve. 3770 * 3771 * @param value The classloader to use for class resolution. Can be <jk>null</jk> to reset to thread-context fallback. 3772 * @return This object. 3773 */ 3774 public Builder classLoader(ClassLoader value) { 3775 classLoader = value; 3776 return this; 3777 } 3778 3779 @Override /* Overridden from Builder */ 3780 public Builder type(Class<? extends Context> value) { 3781 reqnn("value", value); 3782 super.type(value); 3783 return this; 3784 } 3785 3786 /** 3787 * An identifying name for this class. 3788 * 3789 * <p> 3790 * The name is used to identify the class type during parsing when it cannot be inferred through reflection. 3791 * For example, if a bean property is of type <c>Object</c>, then the serializer will add the name to the 3792 * output so that the class can be determined during parsing. 3793 * 3794 * <p> 3795 * It is also used to specify element names in XML. 3796 * 3797 * <h5 class='section'>Example:</h5> 3798 * <p class='bjava'> 3799 * <jc>// Use _type='mybean' to identify this bean.</jc> 3800 * <jk>public class</jk> MyBean {...} 3801 * 3802 * <jc>// Create a serializer and specify the type name..</jc> 3803 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3804 * .<jsm>create</jsm>() 3805 * .typeName(MyBean.<jk>class</jk>, <js>"mybean"</js>) 3806 * .build(); 3807 * 3808 * <jc>// Produces: {"_type":"mybean",...}</jc> 3809 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 3810 * </p> 3811 * 3812 * <h5 class='section'>Notes:</h5><ul> 3813 * <li class='note'>Equivalent to the {@link Marshalled#typeName() @Marshalled(typeName)} annotation. 3814 * </ul> 3815 * 3816 * <h5 class='section'>See Also:</h5><ul> 3817 * <li class='jc'>{@link Marshalled#typeName() @Marshalled(typeName)} 3818 * <li class='jm'>{@link #beanDictionary(Class...)} 3819 * </ul> 3820 * 3821 * @param on 3822 * The class the type name is being defined on. 3823 * <br>Cannot be <jk>null</jk>. 3824 * @param value 3825 * The new value for this setting. 3826 * <br>Cannot be <jk>null</jk>. 3827 * @return This object. 3828 */ 3829 public Builder typeName(Class<?> on, String value) { 3830 reqnn("on", on); 3831 reqnn("value", value); 3832 return annotations(MarshalledApplyAnnotation.create(on).value(MarshalledAnnotation.create().typeName(value).build()).build()); 3833 } 3834 3835 /** 3836 * Bean type property name. 3837 * 3838 * <p> 3839 * Same as {@link #typePropertyName(String)} except targets a specific bean class instead of globally. 3840 * 3841 * <h5 class='section'>Example:</h5> 3842 * <p class='bjava'> 3843 * <jc>// POJOs with @Marshalled(name) annotations.</jc> 3844 * <ja>@Marshalled</ja>(typeName=<js>"foo"</js>) 3845 * <jk>public class</jk> Foo {...} 3846 * <ja>@Marshalled</ja>(typeName=<js>"bar"</js>) 3847 * <jk>public class</jk> Bar {...} 3848 * 3849 * <jc>// A bean with a field with an indeterminate type.</jc> 3850 * <jk>public class</jk> MyBean { 3851 * <jk>public</jk> Object <jf>mySimpleField</jf>; 3852 * } 3853 * 3854 * <jc>// Create a serializer that uses 't' instead of '_type' for dictionary names.</jc> 3855 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3856 * .<jsm>create</jsm>() 3857 * .typePropertyName(MyBean.<jk>class</jk>, <js>"t"</js>) 3858 * .dictionary(Foo.<jk>class</jk>, Bar.<jk>class</jk>) 3859 * .build(); 3860 * 3861 * <jc>// Produces "{mySimpleField:{t:'foo',...}}".</jc> 3862 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 3863 * </p> 3864 * 3865 * <h5 class='section'>See Also:</h5><ul> 3866 * <li class='ja'>{@link Marshalled#typePropertyName() @Marshalled(typePropertyName)} 3867 * </ul> 3868 * 3869 * @param on The class the type property name applies to. 3870 * <br>Cannot be <jk>null</jk>. 3871 * @param value 3872 * The new value for this setting. 3873 * <br>The default is <js>"_type"</js>. 3874 * <br>Cannot be <jk>null</jk>. 3875 * @return This object. 3876 */ 3877 public Builder typePropertyName(Class<?> on, String value) { 3878 reqnn("on", on); 3879 reqnn("value", value); 3880 return annotations(MarshalledApplyAnnotation.create(on).value(MarshalledAnnotation.create().typePropertyName(value).build()).build()); 3881 } 3882 3883 /** 3884 * Bean type property name. 3885 * 3886 * <p> 3887 * This specifies the name of the bean property used to store the dictionary name of a bean type so that the 3888 * parser knows the data type to reconstruct. 3889 * 3890 * <h5 class='section'>Example:</h5> 3891 * <p class='bjava'> 3892 * <jc>// POJOs with @Marshalled(name) annotations.</jc> 3893 * <ja>@Marshalled</ja>(typeName=<js>"foo"</js>) 3894 * <jk>public class</jk> Foo {...} 3895 * <ja>@Marshalled</ja>(typeName=<js>"bar"</js>) 3896 * <jk>public class</jk> Bar {...} 3897 * 3898 * <jc>// Create a serializer that uses 't' instead of '_type' for dictionary names.</jc> 3899 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3900 * .<jsm>create</jsm>() 3901 * .typePropertyName(<js>"t"</js>) 3902 * .dictionary(Foo.<jk>class</jk>, Bar.<jk>class</jk>) 3903 * .build(); 3904 * 3905 * <jc>// Create a serializer that uses 't' instead of '_type' for dictionary names.</jc> 3906 * ReaderParser <jv>parser</jv> = JsonParser 3907 * .<jsm>create</jsm>() 3908 * .typePropertyName(<js>"t"</js>) 3909 * .dictionary(Foo.<jk>class</jk>, Bar.<jk>class</jk>) 3910 * .build(); 3911 * 3912 * <jc>// A bean with a field with an indeterminate type.</jc> 3913 * <jk>public class</jk> MyBean { 3914 * <jk>public</jk> Object <jf>mySimpleField</jf>; 3915 * } 3916 * 3917 * <jc>// Produces "{mySimpleField:{t:'foo',...}}".</jc> 3918 * String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean()); 3919 * 3920 * <jc>// Parse bean.</jc> 3921 * MyBean <jv>bean</jv> = <jv>parser</jv>.read(<jv>json</jv>, MyBean.<jk>class</jk>); 3922 * </p> 3923 * 3924 * <h5 class='section'>See Also:</h5><ul> 3925 * <li class='ja'>{@link Marshalled#typePropertyName()} 3926 * <li class='ja'>{@link MarshalledConfig#typePropertyName()} 3927 * </ul> 3928 * 3929 * @param value 3930 * The new value for this setting. 3931 * <br>The default is <js>"_type"</js>. 3932 * <br>Cannot be <jk>null</jk>. 3933 * @return This object. 3934 */ 3935 public Builder typePropertyName(String value) { 3936 typePropertyName = reqnn("value", value); 3937 return this; 3938 } 3939 3940 /** 3941 * Use Java Introspector. 3942 * 3943 * <p> 3944 * Using the built-in Java bean introspector will not pick up fields or non-standard getters/setters. 3945 * <br>Most {@link Bean @Marshalled} annotations will be ignored. 3946 * 3947 * <h5 class='section'>Example:</h5> 3948 * <p class='bjava'> 3949 * <jc>// Create a serializer that only uses the built-in java bean introspector for finding properties.</jc> 3950 * WriterSerializer <jv>serializer</jv> = JsonSerializer 3951 * .<jsm>create</jsm>() 3952 * .useJavaBeanIntrospector() 3953 * .build(); 3954 * </p> 3955 * 3956 * @return This object. 3957 */ 3958 public Builder useJavaBeanIntrospector() { 3959 return useJavaBeanIntrospector(true); 3960 } 3961 3962 /** 3963 * Same as {@link #useJavaBeanIntrospector()} but allows you to explicitly specify the value. 3964 * 3965 * @param value The value for this setting. 3966 * @return This object. 3967 */ 3968 public Builder useJavaBeanIntrospector(boolean value) { 3969 useJavaBeanIntrospector = value; 3970 return this; 3971 } 3972 } 3973 3974 /* 3975 * The default package pattern exclusion list. 3976 * Any beans in packages in this list will not be considered beans. 3977 */ 3978 // @formatter:off 3979 private static final List<String> DEFAULT_NOTBEAN_PACKAGES = l( 3980 "java.lang", 3981 "java.lang.annotation", 3982 "java.lang.ref", 3983 "java.lang.reflect", 3984 "java.io", 3985 "java.net", 3986 "java.nio.*", 3987 "java.util.*" 3988 ); 3989 // @formatter:on 3990 3991 /* 3992 * The default bean class exclusion list. 3993 * Anything in this list will not be considered beans. 3994 */ 3995 // @formatter:off 3996 private static final List<ClassInfo> DEFAULT_NOTBEAN_CLASSES = l( 3997 info(Map.class), 3998 info(Collection.class), 3999 info(Reader.class), 4000 info(Writer.class), 4001 info(InputStream.class), 4002 info(OutputStream.class), 4003 info(Throwable.class) 4004 ); 4005 // @formatter:on 4006 4007 /** Default config. All default settings. */ 4008 public static final MarshallingContext DEFAULT = create().build(); 4009 4010 /** Default reusable unmodifiable session. Can be used to avoid overhead of creating a session (for creating BeanMaps for example).*/ 4011 public static final MarshallingSession DEFAULT_SESSION = DEFAULT.createSession().unmodifiable().build(); 4012 4013 /** 4014 * Creates a new builder for this object. 4015 * 4016 * @return A new builder. 4017 */ 4018 public static Builder create() { 4019 return new Builder(); 4020 } 4021 4022 /** Runs the @BeanProp(required) check for a Map-to-bean conversion, adding the parse position when called from a parser. */ 4023 private static <T> BeanMap<T> checkRequired(MarshallingSession bs, BeanMap<T> bm) { 4024 return MissingRequiredPropertyException.check(bs instanceof ParserSession ps ? ps : null, bm); 4025 } 4026 4027 /** 4028 * Checks if the specified class should be cached. 4029 * 4030 * <p> 4031 * Generated classes (proxies, lambda expressions, etc.) shouldn't be cacheable to prevent 4032 * needlessly filling up the cache. 4033 * 4034 * @param c The class to check. 4035 * @return <jk>true</jk> if the class should be cached. 4036 */ 4037 private static boolean isCacheable(Class<?> c) { 4038 var n = c.getName(); 4039 var x = n.charAt(n.length() - 1); // All generated classes appear to end with digits. 4040 return !((x >= '0' && x <= '9') && (n.indexOf("$$") != -1 || n.startsWith("sun") || n.startsWith("com.sun") || n.indexOf("$Proxy") != -1)); 4041 } 4042 4043 private final NullableSupplier<WriterSerializer> beanToStringSerializer; 4044 private final NullableSupplier<BeanConfigContext> beanConfigContext; 4045 private final BeanRegistry beanRegistry; 4046 private final MarshallingSession defaultSession; 4047 private final ConfigurableConverter converter; 4048 private final boolean beanMapPutReturnsOldValue; 4049 private final boolean beansRequireDefaultConstructor; 4050 private final boolean beansRequireSerializable; 4051 private final boolean beansRequireSettersForGetters; 4052 private final boolean beansRequireSomeProperties; 4053 private final boolean findFluentSetters; 4054 private final boolean ignoreInvocationExceptionsOnGetters; 4055 private final boolean ignoreInvocationExceptionsOnSetters; 4056 private final boolean ignoreMissingSetters; 4057 private final boolean ignoreTransientFields; 4058 private final boolean ignoreUnknownBeanProperties; 4059 private final boolean ignoreUnknownEnumValues; 4060 private final boolean ignoreUnknownNullBeanProperties; 4061 private final boolean unsortedProperties; 4062 private final boolean recordComponentOrder; 4063 private final boolean useInterfaceProxies; 4064 private final boolean useJavaBeanIntrospector; 4065 private final boolean validateSchema; 4066 private final Class<? extends PropertyNamer> propertyNamer; 4067 private final ClassMeta<Object> cmObject; // Reusable ClassMeta that represents general Objects. 4068 private final ClassMeta<String> cmString; // Reusable ClassMeta that represents general Strings. 4069 private final HashKey hashKey; 4070 private final List<ClassInfo> beanDictionary; 4071 private final List<ClassInfo> notBeanClasses; 4072 private final List<Object> swaps; 4073 private final List<String> notBeanPackages; 4074 private final Locale locale; 4075 private final Cache<Class,ClassMeta> cmCache; 4076 private final MediaType mediaType; 4077 private final List<ObjectSwap<?,?>> objectSwaps; 4078 private final PropertyNamer propertyNamerBean; 4079 private final String typePropertyName; 4080 private final Set<String> notBeanPackageNames; 4081 private final List<String> notBeanPackagePrefixes; 4082 private final TimeZone timeZone; 4083 private final DurationFormat durationFormat; 4084 private final PeriodFormat periodFormat; 4085 private final CalendarFormat calendarFormat; 4086 private final DateFormat dateFormat; 4087 private final TemporalFormat temporalFormat; 4088 private final TimeZoneFormat timeZoneFormat; 4089 private final LocaleFormat localeFormat; 4090 private final BinaryFormat binaryFormat; 4091 private final EnumFormat enumFormat; 4092 private final UuidFormat uuidFormat; 4093 private final BitSetFormat bitSetFormat; 4094 private final BigNumberFormat bigNumberFormat; 4095 private final BooleanFormat booleanFormat; 4096 private final FloatFormat floatFormat; 4097 private final CurrencyFormat currencyFormat; 4098 private final ClassFormat classFormat; 4099 private final ClassLoader classLoader; 4100 private final Visibility beanClassVisibility; 4101 private final Visibility beanConstructorVisibility; 4102 private final Visibility beanFieldVisibility; 4103 private final Visibility beanMethodVisibility; 4104 private final BeanStore beanStore; 4105 private final String activeView; 4106 private final boolean defaultViewInclusion; 4107 4108 /** 4109 * Constructor. 4110 * 4111 * @param builder The builder for this object. 4112 * <br>Cannot be <jk>null</jk>. 4113 */ 4114 public MarshallingContext(Builder builder) { 4115 super(builder); 4116 4117 beanClassVisibility = builder.beanClassVisibility; 4118 beanConstructorVisibility = builder.beanConstructorVisibility; 4119 beanDictionary = u(cp(builder.beanDictionary)); 4120 beanFieldVisibility = builder.beanFieldVisibility; 4121 beanMapPutReturnsOldValue = builder.beanMapPutReturnsOldValue; 4122 beanMethodVisibility = builder.beanMethodVisibility; 4123 beansRequireDefaultConstructor = builder.beansRequireDefaultConstructor; 4124 beansRequireSerializable = builder.beansRequireSerializable; 4125 beansRequireSettersForGetters = builder.beansRequireSettersForGetters; 4126 beansRequireSomeProperties = ! builder.disableBeansRequireSomeProperties; 4127 findFluentSetters = builder.findFluentSetters; 4128 hashKey = builder.hashKey(); 4129 ignoreInvocationExceptionsOnGetters = builder.ignoreInvocationExceptionsOnGetters; 4130 ignoreInvocationExceptionsOnSetters = builder.ignoreInvocationExceptionsOnSetters; 4131 ignoreMissingSetters = ! builder.disableIgnoreMissingSetters; 4132 ignoreTransientFields = ! builder.disableIgnoreTransientFields; 4133 ignoreUnknownBeanProperties = builder.ignoreUnknownBeanProperties; 4134 ignoreUnknownEnumValues = builder.ignoreUnknownEnumValues; 4135 ignoreUnknownNullBeanProperties = ! builder.disableIgnoreUnknownNullBeanProperties; 4136 locale = o(builder.locale).orElse(Locale.getDefault()); 4137 mediaType = builder.mediaType; 4138 notBeanPackages = u(new ArrayList<>(builder.notBeanPackages)); 4139 propertyNamer = nn(builder.propertyNamer) ? builder.propertyNamer : BasicPropertyNamer.class; 4140 unsortedProperties = builder.unsortedProperties; 4141 recordComponentOrder = builder.recordComponentOrder; 4142 swaps = u(cp(builder.swaps)); 4143 timeZone = builder.timeZone; 4144 durationFormat = builder.durationFormat; 4145 periodFormat = builder.periodFormat; 4146 calendarFormat = builder.calendarFormat; 4147 dateFormat = builder.dateFormat; 4148 temporalFormat = builder.temporalFormat; 4149 timeZoneFormat = builder.timeZoneFormat; 4150 localeFormat = builder.localeFormat; 4151 binaryFormat = builder.binaryFormat; 4152 enumFormat = builder.enumFormat; 4153 uuidFormat = builder.uuidFormat; 4154 bitSetFormat = builder.bitSetFormat; 4155 bigNumberFormat = builder.bigNumberFormat; 4156 booleanFormat = builder.booleanFormat; 4157 floatFormat = builder.floatFormat; 4158 currencyFormat = builder.currencyFormat; 4159 classFormat = builder.classFormat; 4160 classLoader = builder.classLoader; 4161 typePropertyName = o(builder.typePropertyName).orElse("_type"); 4162 useInterfaceProxies = ! builder.disableInterfaceProxies; 4163 useJavaBeanIntrospector = builder.useJavaBeanIntrospector; 4164 validateSchema = builder.validateSchema; 4165 beanStore = builder.beanStore; 4166 activeView = builder.activeView; 4167 defaultViewInclusion = ! builder.disableDefaultViewInclusion; 4168 4169 var builderNotBeanClasses = new ArrayList<>(builder.notBeanClasses); 4170 notBeanClasses = builderNotBeanClasses.isEmpty() ? DEFAULT_NOTBEAN_CLASSES : Stream.concat(builderNotBeanClasses.stream(), DEFAULT_NOTBEAN_CLASSES.stream()).distinct().toList(); 4171 4172 List<String> notBeanPackagesList = notBeanPackages.isEmpty() ? DEFAULT_NOTBEAN_PACKAGES : Stream.concat(notBeanPackages.stream(), DEFAULT_NOTBEAN_PACKAGES.stream()).toList(); 4173 LinkedHashSet<String> notBeanPackageNamesTemp = notBeanPackagesList.stream().filter(x -> ! x.endsWith(".*")).collect(Collectors.toCollection(LinkedHashSet::new)); 4174 notBeanPackageNames = u(notBeanPackageNamesTemp); 4175 notBeanPackagePrefixes = notBeanPackagesList.stream().filter(x -> x.endsWith(".*")).map(x -> x.substring(0, x.length() - 2)).toList(); 4176 4177 propertyNamerBean = safe(()->propertyNamer.getDeclaredConstructor().newInstance()); 4178 4179 var objectSwapsList = new LinkedList<ObjectSwap<?,?>>(); 4180 swaps.forEach(x -> { 4181 if (x instanceof ObjectSwap<?,?> x2) { 4182 objectSwapsList.add(x2); 4183 } else { 4184 var ci = info((Class<?>)x); 4185 if (ci.isAssignableTo(ObjectSwap.class)) 4186 objectSwapsList.add(BeanInstantiator.of(ObjectSwap.class).type(ci).run()); 4187 else if (ci.isAssignableTo(Surrogate.class)) 4188 objectSwapsList.addAll(SurrogateSwap.findObjectSwaps(ci.inner(), this)); 4189 else 4190 throw rex("Invalid class %s specified in MarshallingContext.swaps property. Must be a subclass of ObjectSwap or Surrogate.", cn(ci.inner())); 4191 } 4192 }); 4193 objectSwaps = u(objectSwapsList); 4194 converter = new ConfigurableConverter(this); 4195 4196 cmCache = Cache.<Class,ClassMeta>create().supplier(type -> new ClassMeta<>(type, this)).build(); 4197 cmString = cmCache.get(String.class); 4198 cmObject = cmCache.get(Object.class); 4199 4200 beanRegistry = new BeanRegistry(this, null, list()); 4201 defaultSession = createSession().unmodifiable().build(); 4202 beanToStringSerializer = memoize(() -> Json5Serializer.create().marshallingContext(this).build()); 4203 beanConfigContext = memoize(this::buildBeanConfigContext); 4204 } 4205 4206 /** 4207 * Builds the {@link BeanConfigContext} snapshot exposed by {@link #getBeanConfigContext()}. 4208 * 4209 * Lifts the bean-modeling subset of this context's settings into a portable POJO that the bean-modeling 4210 * runtime in {@code commons.bean} can consume without referencing any marshalling-aware types. 4211 */ 4212 private BeanConfigContext buildBeanConfigContext() { 4213 return BeanConfigContext.create() 4214 .beanClassVisibility(beanClassVisibility) 4215 .beanConstructorVisibility(beanConstructorVisibility) 4216 .beanFieldVisibility(beanFieldVisibility) 4217 .beanMethodVisibility(beanMethodVisibility) 4218 .beanMapPutReturnsOldValue(beanMapPutReturnsOldValue) 4219 .beansRequireDefaultConstructor(beansRequireDefaultConstructor) 4220 .beansRequireSerializable(beansRequireSerializable) 4221 .beansRequireSettersForGetters(beansRequireSettersForGetters) 4222 .beansRequireSomeProperties(beansRequireSomeProperties) 4223 .findFluentSetters(findFluentSetters) 4224 .ignoreInvocationExceptionsOnGetters(ignoreInvocationExceptionsOnGetters) 4225 .ignoreInvocationExceptionsOnSetters(ignoreInvocationExceptionsOnSetters) 4226 .ignoreMissingSetters(ignoreMissingSetters) 4227 .ignoreTransientFields(ignoreTransientFields) 4228 .ignoreUnknownBeanProperties(ignoreUnknownBeanProperties) 4229 .ignoreUnknownNullBeanProperties(ignoreUnknownNullBeanProperties) 4230 .unsortedProperties(unsortedProperties) 4231 .recordComponentOrder(recordComponentOrder) 4232 .useInterfaceProxies(useInterfaceProxies) 4233 .useJavaBeanIntrospector(useJavaBeanIntrospector) 4234 .propertyNamer(propertyNamerBean) 4235 .beanTypePropertyName(typePropertyName) 4236 .notBeanPackageNames(notBeanPackageNames) 4237 .notBeanPackagePrefixes(notBeanPackagePrefixes) 4238 .notBeanClasses(notBeanClasses.stream().map(ClassInfo::inner).toList()) 4239 .beanStore(beanStore) 4240 .annotationProvider(getAnnotationProvider()) 4241 .beanMetaInitializer(MarshalledBeanMetaInitializer.INSTANCE) 4242 .beanPropertyPostProcessor(MarshalledPropertyPostProcessor.INSTANCE) 4243 .build(); 4244 } 4245 4246 /** 4247 * Converts the specified value to the specified class type. 4248 * 4249 * <p> 4250 * This is a shortcut for the following code: <c>createSession().build().convertToType(<jv>value</jv>, <jv>type</jv>);</c> 4251 * 4252 * @param <T> The class type to convert the value to. 4253 * @param value The value to convert. 4254 * <br>Can be <jk>null</jk> (returns <jk>null</jk>). 4255 * @param type The class type to convert the value to. 4256 * <br>Cannot be <jk>null</jk>. 4257 * @throws InvalidDataConversionException If the specified value cannot be converted to the specified type. 4258 * @return The converted value. 4259 * @see MarshallingSession#convertToType(Object, Class) 4260 */ 4261 public final <T> T convertToType(Object value, Class<T> type) throws InvalidDataConversionException { 4262 return defaultSession.convertToType(value, type); 4263 } 4264 4265 @Override /* Overridden from Context */ 4266 public Builder copy() { 4267 return new Builder(this); 4268 } 4269 4270 @Override /* Overridden from Context */ 4271 public MarshallingSession.Builder<?> createSession() { 4272 return MarshallingSession.create(this); 4273 } 4274 4275 /** 4276 * Minimum bean class visibility. 4277 * 4278 * @see MarshallingContext.Builder#beanClassVisibility(Visibility) 4279 * @return 4280 * Classes are not considered beans unless they meet the minimum visibility requirements. 4281 */ 4282 public final Visibility getBeanClassVisibility() { return beanClassVisibility; } 4283 4284 /** 4285 * Minimum bean constructor visibility. 4286 * 4287 * @see MarshallingContext.Builder#beanConstructorVisibility(Visibility) 4288 * @return 4289 * Only look for constructors with this specified minimum visibility. 4290 */ 4291 public final Visibility getBeanConstructorVisibility() { return beanConstructorVisibility; } 4292 4293 /** 4294 * The bean store used for factory-based instantiation. 4295 * 4296 * @see MarshallingContext.Builder#beanStore(BeanStore) 4297 * @return The bean store, or <jk>null</jk> if none configured. 4298 */ 4299 public final BeanStore getBeanStore() { return beanStore; } 4300 4301 /** 4302 * Bean dictionary. 4303 * 4304 * @see MarshallingContext.Builder#beanDictionary() 4305 * @return 4306 * The list of classes that make up the bean dictionary in this bean context. 4307 * <br>Never <jk>null</jk>. 4308 * <br>List is unmodifiable. 4309 */ 4310 public final List<ClassInfo> getBeanDictionary() { return beanDictionary; } 4311 4312 /** 4313 * Returns the {@link BeanConfigContext} snapshot for this marshalling context. 4314 * 4315 * <p> 4316 * The snapshot lifts the bean-modeling subset of this context's settings into a portable POJO that the 4317 * bean-modeling runtime in {@code commons.bean} can consume without referencing any marshalling-aware types. 4318 * It is computed lazily on first access and cached for the lifetime of this context. 4319 * 4320 * <p> 4321 * Mutating the returned snapshot's settings (via {@link BeanConfigContext#copy()} and a fresh build) does not 4322 * affect this marshalling context. 4323 * 4324 * @return The bean-modeling configuration snapshot. Never <jk>null</jk>. 4325 */ 4326 public final BeanConfigContext getBeanConfigContext() { return beanConfigContext.get(); } 4327 4328 /** 4329 * Minimum bean field visibility. 4330 * 4331 * 4332 * @see MarshallingContext.Builder#beanFieldVisibility(Visibility) 4333 * @return 4334 * Only look for bean fields with this specified minimum visibility. 4335 */ 4336 public final Visibility getBeanFieldVisibility() { return beanFieldVisibility; } 4337 4338 /** 4339 * Returns the converter for this context. 4340 * 4341 * @return The converter. 4342 */ 4343 public final ConfigurableConverter getConverter() { return converter; } 4344 4345 /** 4346 * Fallback for an ObjectSwap-conversion whose swap-class doesn't bridge to the input, but where the input 4347 * is already a {@link Map} matching the target's own Map shape -- copies its entries into a new instance 4348 * of the target type rather than dropping the value as {@code null}. 4349 * 4350 * @param toMeta The target type. 4351 * @param in The input value. 4352 * @return A new map instance of the target type populated with the input's entries, or <jk>null</jk> if 4353 * the target isn't Map-shaped or the input isn't a {@link Map}. 4354 */ 4355 private static Object copyMapEntries(ClassMeta<?> toMeta, Object in) { 4356 if (! (toMeta.isMap() && in instanceof Map<?,?> in2)) 4357 return null; 4358 var result = (Map<Object,Object>) (toMeta.canCreateNewInstance() ? toMeta.newInstance() : new LinkedHashMap<>()); 4359 result.putAll(in2); 4360 return result; 4361 } 4362 4363 /** 4364 * Implements {@link ConversionFinder} to provide all MarshallingContext-specific type conversions that cannot 4365 * be expressed in {@link BasicConverter} (which has no dependency on {@code juneau-marshall}). 4366 * 4367 * <p> 4368 * Conversions are resolved in this priority order: 4369 * <ol> 4370 * <li>ObjectSwap-based conversions (highest priority) 4371 * <li>Iso8601 date/time formatting and parsing 4372 * <li>Epoch-millis to date/time 4373 * <li>Map/Bean/Collection/Array to String via serializer 4374 * <li>String/CharSequence to Array, Map, Collection via JSON parsing 4375 * <li>Map to Bean and String to Bean via BeanMap 4376 * <li>Calendar/Date specialized copies 4377 * <li>InputStream and Reader construction 4378 * </ol> 4379 * 4380 * @param inType The input type class. 4381 * @param outType The output type class. 4382 * @return A matching {@link Conversion}, or {@code null} if none applies. 4383 */ 4384 @Override 4385 @SuppressWarnings({ 4386 "java:S6541", // Brain Method: conversion dispatch inherently requires handling many type pairs in one place 4387 "null" // `builder` is guarded by `nn(builder)` before access; Eclipse doesn't recognise `nn()` as a null-check function. 4388 }) 4389 public Conversion<?,?> findConversion(Class<?> inType, Class<?> outType) { 4390 var toMeta = getClassMeta(outType); 4391 var fromMeta = getClassMeta(inType); 4392 4393 // --- ObjectSwap conversions (must come first) --- 4394 for (var swap : objectSwaps) { 4395 var nc = swap.getNormalClass().inner(); 4396 var fc = swap.getSwapClass().inner(); 4397 // Unswap: input is swap class (or convertible to it), output is normal class 4398 if (nc.isAssignableFrom(outType) && (fc.isAssignableFrom(inType) || Map.class.isAssignableFrom(inType) || Number.class.isAssignableFrom(inType))) { 4399 return (in, memberOf, session, args) -> { 4400 try { 4401 var bs = marshallingSession(session); 4402 var resolvedSwap = bs != null ? toMeta.getSwap(bs) : null; 4403 var activeSwap = (ObjectSwap<Object,Object>) or(resolvedSwap, swap); 4404 var swapClass = activeSwap.getSwapClass().inner(); 4405 // Direct match 4406 if (swapClass.isInstance(in)) 4407 return activeSwap.unswap(bs, in, toMeta); 4408 // Intermediate Map conversion 4409 if (Map.class.isAssignableFrom(swapClass) && in instanceof Map) 4410 return activeSwap.unswap(bs, converter.to(in, memberOf, bs, swapClass), toMeta); 4411 // Intermediate Number conversion 4412 if (Number.class.isAssignableFrom(swapClass) && in instanceof Number) 4413 return activeSwap.unswap(bs, converter.to(in, memberOf, bs, swapClass), toMeta); 4414 // The swap's own swap-class is shaped like neither Map nor Number, so it doesn't apply to 4415 // this input -- but if the input is already a Map matching toMeta's own Map shape, copy 4416 // its entries directly rather than dropping the value. 4417 return copyMapEntries(toMeta, in); 4418 } catch (Exception e) { 4419 throw rex(e); 4420 } 4421 }; 4422 } 4423 // Swap: input is normal class, output is swap class 4424 if (nc.isAssignableFrom(inType) && fc.isAssignableFrom(outType)) { 4425 return (in, memberOf, session, args) -> { 4426 try { 4427 var bs = marshallingSession(session); 4428 return ((ObjectSwap<Object,Object>) swap).swap(bs, in); 4429 } catch (Exception e) { 4430 throw rex(e); 4431 } 4432 }; 4433 } 4434 } 4435 4436 // --- Inline/auto-detected swaps from ClassMeta (e.g. AutoNumberSwap, AutoObjectSwap) --- 4437 // These are swaps found on the class itself (via swap() methods, toXxx() patterns, etc.) 4438 // and not registered in the objectSwaps list. 4439 var toSwap = toMeta.getSwap(null); 4440 if (toSwap != null) { 4441 // Use boxed version of swap class to handle primitive swap types (e.g. toInt() → int.class → Integer.class) 4442 var fcBoxed = info(toSwap.getSwapClass().inner()).getWrapperIfPrimitive().inner(); 4443 if (fcBoxed.isAssignableFrom(inType) || Map.class.isAssignableFrom(inType) || Number.class.isAssignableFrom(inType)) { 4444 return (in, memberOf, session, args) -> { 4445 try { 4446 var bs = marshallingSession(session); 4447 var activeSwap = (ObjectSwap<Object,Object>) toMeta.getSwap(bs); 4448 if (activeSwap == null) return null; 4449 // Use boxed swap class to handle primitive return types (e.g. int.class → Integer.class) 4450 var swapClass = info(activeSwap.getSwapClass().inner()).getWrapperIfPrimitive().inner(); 4451 if (swapClass.isInstance(in)) 4452 return activeSwap.unswap(bs, in, toMeta); 4453 if (Map.class.isAssignableFrom(swapClass) && in instanceof Map) 4454 return activeSwap.unswap(bs, converter.to(in, memberOf, bs, swapClass), toMeta); 4455 if (Number.class.isAssignableFrom(swapClass) && in instanceof Number) 4456 return activeSwap.unswap(bs, converter.to(in, memberOf, bs, swapClass), toMeta); 4457 // See the analogous fallback in the objectSwaps loop above. 4458 return copyMapEntries(toMeta, in); 4459 } catch (Exception e) { 4460 throw rex(e); 4461 } 4462 }; 4463 } 4464 } 4465 var fromSwap = fromMeta.getSwap(null); 4466 if (fromSwap != null) { 4467 var fcBoxed = info(fromSwap.getSwapClass().inner()).getWrapperIfPrimitive().inner(); 4468 if (fcBoxed.isAssignableFrom(outType) || outType == Object.class) { 4469 return (in, memberOf, session, args) -> { 4470 try { 4471 var bs = marshallingSession(session); 4472 var activeSwap = (ObjectSwap<Object,Object>) fromMeta.getSwap(bs); 4473 if (activeSwap == null) return null; 4474 return activeSwap.swap(bs, in); 4475 } catch (Exception e) { 4476 throw rex(e); 4477 } 4478 }; 4479 } 4480 } 4481 4482 // --- Date/time → CharSequence via Iso8601Utils (Gap 5) --- 4483 if (CharSequence.class.isAssignableFrom(outType) && (fromMeta.isDateOrCalendarOrTemporal() || fromMeta.isDuration())) 4484 return (in, memberOf, session, args) -> Iso8601Utils.format(in, fromMeta, sessionTimeZone(session)); 4485 4486 // --- CharSequence → date/time via Iso8601Utils (Gap 6) --- 4487 if (CharSequence.class.isAssignableFrom(inType) && (toMeta.isDateOrCalendarOrTemporal() || toMeta.isDuration())) 4488 return (in, memberOf, session, args) -> Iso8601Utils.parse(in.toString(), toMeta, sessionTimeZone(session)); 4489 4490 // --- Number → date/time via epoch millis (Gap 7) --- 4491 if (Number.class.isAssignableFrom(inType) && toMeta.isDateOrCalendarOrTemporal()) 4492 return (in, memberOf, session, args) -> Iso8601Utils.fromEpochMillis(((Number)in).longValue(), toMeta, sessionTimeZone(session)); 4493 4494 // --- Map/Bean/Collection/Array/Optional → String via serializer (Gap 14) --- 4495 if (outType == String.class) { 4496 // byte[] → String must come before isCollectionOrArrayOrOptional since byte[] is an array type 4497 if (byte[].class == inType) 4498 return (in, memberOf, session, args) -> new String((byte[]) in, StandardCharsets.UTF_8); 4499 if (fromMeta.isMapOrBean() || fromMeta.isCollectionOrArrayOrOptional()) { 4500 return (in, memberOf, session, args) -> { 4501 try { 4502 var ws = getBeanToStringSerializer(); 4503 return ws != null ? ws.write(in) : in.toString(); 4504 } catch (Exception e) { 4505 throw rex(e); 4506 } 4507 }; 4508 } 4509 if (Class.class == inType) 4510 return (in, memberOf, session, args) -> ((Class<?>) in).getName(); 4511 } 4512 4513 // --- CharSequence → Array via JSON parsing (Gap 10) --- 4514 // Excludes byte[] since that is handled by BasicConverter.findSpecialConversion (getBytes(UTF_8)). 4515 if (CharSequence.class.isAssignableFrom(inType) && outType.isArray() && outType != byte[].class) { 4516 return (in, memberOf, session, args) -> { 4517 try { 4518 var bs = beanSessionOrDefault(session); 4519 var str = in.toString(); 4520 Json5List list; 4521 if (startsWith(str, '[')) 4522 list = Json5List.ofString(str).setBeanSession(bs); 4523 else 4524 list = new Json5List((Object[]) splita(str)).setBeanSession(bs); 4525 return bs.convertToType(list, outType); 4526 } catch (Exception e) { 4527 throw rex(e); 4528 } 4529 }; 4530 } 4531 4532 // --- CharSequence → Map via JSON parsing (Gap 11) --- 4533 if (CharSequence.class.isAssignableFrom(inType) && Map.class.isAssignableFrom(outType) && !toMeta.canCreateNewInstanceFromString(null)) { 4534 return (in, memberOf, session, args) -> { 4535 try { 4536 var bs = beanSessionOrDefault(session); 4537 var m = Json5Map.ofString(in.toString()); 4538 m.setBeanSession(bs); 4539 return bs.convertToType(m, outType); 4540 } catch (Exception e) { 4541 throw rex(e); 4542 } 4543 }; 4544 } 4545 4546 // --- Collection/Array → Collection with session-aware element conversion --- 4547 // When the target is an abstract collection type (List, Collection), uses JsonList instead of ArrayList 4548 // to preserve the session-aware default behavior from the original convertToCollectionType. 4549 if ((Collection.class.isAssignableFrom(inType) || inType.isArray()) && Collection.class.isAssignableFrom(outType)) { 4550 return (in, memberOf, session, args) -> { 4551 // Treat Object.class as "no element type" (untyped/raw, needs no element conversion). 4552 var elemType = args.length > 0 && args[0] != Object.class ? (Class<?>) args[0] : null; 4553 if (elemType == null && !inType.isArray() && outType.isAssignableFrom(in.getClass())) 4554 return in; 4555 var bs = beanSessionOrDefault(session); 4556 Collection<Object> result; 4557 if (outType.isAssignableFrom(JsonList.class)) { 4558 result = new JsonList(bs); // Use JsonList for abstract types (List, Collection, Iterable) 4559 } else { 4560 result = newCollection(outType); 4561 } 4562 if (in instanceof Collection<?> col2) { 4563 for (var elem : col2) 4564 result.add(elemType != null ? converter.to(elem, memberOf, session, elemType) : elem); 4565 } else { 4566 var len = Array.getLength(in); 4567 for (var i = 0; i < len; i++) { 4568 var elem = Array.get(in, i); 4569 result.add(elemType != null ? converter.to(elem, memberOf, session, elemType) : elem); 4570 } 4571 } 4572 return result; 4573 }; 4574 } 4575 4576 // --- Map → Collection (wraps map as single element, preserving old convertToCollectionType behavior) --- 4577 if (Map.class.isAssignableFrom(inType) && Collection.class.isAssignableFrom(outType)) { 4578 return (in, memberOf, session, args) -> { 4579 var bs = beanSessionOrDefault(session); 4580 Collection<Object> result; 4581 if (outType.isAssignableFrom(JsonList.class)) { 4582 result = new JsonList(bs); 4583 } else { 4584 result = newCollection(outType); 4585 } 4586 var elemType = args.length > 0 && args[0] != Object.class ? (Class<?>) args[0] : null; 4587 result.add(elemType != null ? converter.to(in, memberOf, session, elemType) : in); 4588 return result; 4589 }; 4590 } 4591 4592 // --- CharSequence → Collection via JSON parsing (Gap 12) --- 4593 if (CharSequence.class.isAssignableFrom(inType) && Collection.class.isAssignableFrom(outType)) { 4594 return (in, memberOf, session, args) -> { 4595 try { 4596 var bs = beanSessionOrDefault(session); 4597 var str = in.toString(); 4598 if (!isProbablyJsonArray(str, false)) 4599 throw rex("Cannot convert string to %s: %s", outType.getName(), str); 4600 var elemType = args.length > 0 ? args[0] : null; 4601 var l2 = Json5List.ofString(str).setBeanSession(bs); 4602 var result = newCollection(outType); 4603 l2.forEach(x -> result.add(elemType != null && x != null ? converter.to(x, elemType) : x)); 4604 return result; 4605 } catch (Exception e) { 4606 throw rex(e); 4607 } 4608 }; 4609 } 4610 4611 // --- Map → Bean via BeanMap/BuilderSwap/BeanRegistry (Gap 17) --- 4612 if (Map.class.isAssignableFrom(inType) && toMeta.isBean()) { 4613 return (in, memberOf, session, args) -> { 4614 try { 4615 var bs = beanSessionOrDefault(session); 4616 var m2 = (Map<?,?>) in; 4617 var builder = (BuilderSwap<Object,Object>) toMeta.getBuilderSwap(bs); 4618 if (m2 instanceof MarshalledMap mm && builder == null) { 4619 var typeName = mm.getString(bs.getBeanTypePropertyName(toMeta)); 4620 if (nn(typeName)) { 4621 var cm = toMeta.getBeanRegistry().getClassMeta(typeName); 4622 if (nn(cm) && toMeta.isAssignableFrom(cm.inner())) 4623 return mm.cast(cm); 4624 } 4625 } 4626 if (nn(builder)) { 4627 var created = builder.create(bs, toMeta); 4628 if (created != null) { 4629 var bm = bs.toBeanMap(created); 4630 bm.load(m2); 4631 return builder.build(bs, checkRequired(bs, bm).getBean(), toMeta); 4632 } 4633 } 4634 return checkRequired(bs, bs.newBeanMap(toMeta.inner()).load(m2)).getBean(); 4635 } catch (Exception e) { 4636 var mrpe = MissingRequiredPropertyException.find(e); 4637 throw nn(mrpe) ? mrpe : rex(e); 4638 } 4639 }; 4640 } 4641 4642 // --- CharSequence → URL (URL(String) is deprecated in Java 20+, use URI.create().toURL() instead) --- 4643 if (CharSequence.class.isAssignableFrom(inType) && outType == URL.class) 4644 return (in, memberOf, session, args) -> { 4645 try { 4646 return new URI(in.toString()).toURL(); 4647 } catch (Exception e) { 4648 throw rex(e); 4649 } 4650 }; 4651 4652 // --- CharSequence → Bean via BeanMap (Gap 21) --- 4653 if (CharSequence.class.isAssignableFrom(inType) && toMeta.isBean()) { 4654 return (in, memberOf, session, args) -> { 4655 try { 4656 var bs = beanSessionOrDefault(session); 4657 return BeanMapLoader.load(bs.newBeanMap(toMeta.inner()), in.toString()).getBean(); 4658 } catch (Exception e) { 4659 throw rex(e); 4660 } 4661 }; 4662 } 4663 4664 // --- Calendar/Date specialized conversions (Gap 19) --- 4665 if (toMeta.isCalendar()) { 4666 if (Calendar.class.isAssignableFrom(inType)) 4667 return (in, memberOf, session, args) -> { 4668 var c = (Calendar) in; 4669 var c2 = new GregorianCalendar(c.getTimeZone()); 4670 c2.setTime(c.getTime()); 4671 return c2; 4672 }; 4673 if (Date.class.isAssignableFrom(inType)) 4674 return (in, memberOf, session, args) -> { 4675 var c2 = new GregorianCalendar(TimeZone.getDefault()); 4676 c2.setTime((Date) in); 4677 return c2; 4678 }; 4679 if (Temporal.class.isAssignableFrom(inType)) 4680 return (in, memberOf, session, args) -> { 4681 var temporal = (Temporal) in; 4682 Instant instant; 4683 try { 4684 instant = Instant.from(temporal); 4685 } catch (@SuppressWarnings("unused") DateTimeException e) { 4686 // LocalDateTime lacks offset info - interpret using session/system timezone 4687 var tz = sessionTimeZone(session); 4688 var zoneId = tz != null ? tz.toZoneId() : ZoneId.systemDefault(); 4689 instant = LocalDateTime.from(temporal).atZone(zoneId).toInstant(); 4690 } 4691 return Iso8601Utils.fromEpochMillis(instant.toEpochMilli(), toMeta, sessionTimeZone(session)); 4692 }; 4693 } 4694 if (toMeta.isDate() && outType == Date.class) { 4695 if (Calendar.class.isAssignableFrom(inType)) 4696 return (in, memberOf, session, args) -> ((Calendar) in).getTime(); 4697 if (Temporal.class.isAssignableFrom(inType)) 4698 return (in, memberOf, session, args) -> { 4699 var temporal = (Temporal) in; 4700 Instant instant; 4701 try { 4702 instant = Instant.from(temporal); 4703 } catch (@SuppressWarnings("unused") DateTimeException e) { 4704 // LocalDateTime lacks offset info - interpret using session/system timezone 4705 var tz = sessionTimeZone(session); 4706 var zoneId = tz != null ? tz.toZoneId() : ZoneId.systemDefault(); 4707 instant = LocalDateTime.from(temporal).atZone(zoneId).toInstant(); 4708 } 4709 return Date.from(instant); 4710 }; 4711 } 4712 4713 // --- byte[]/CharSequence → InputStream (Gap 18) --- 4714 if (InputStream.class.isAssignableFrom(outType)) { 4715 if (byte[].class == inType) 4716 return (in, memberOf, session, args) -> { var b = (byte[]) in; return new ByteArrayInputStream(b, 0, b.length); }; 4717 if (CharSequence.class.isAssignableFrom(inType)) 4718 return (in, memberOf, session, args) -> { var b = in.toString().getBytes(); return new ByteArrayInputStream(b, 0, b.length); }; 4719 } 4720 4721 // --- byte[]/CharSequence → Reader (Gap 18) --- 4722 if (Reader.class.isAssignableFrom(outType)) { 4723 if (byte[].class == inType) 4724 return (in, memberOf, session, args) -> new StringReader(new String((byte[]) in)); 4725 if (CharSequence.class.isAssignableFrom(inType)) 4726 return (in, memberOf, session, args) -> new StringReader(in.toString()); 4727 } 4728 4729 // --- CharSequence → Enum using ClassMeta (respects ignoreUnknownEnumValues) (Gap 13) --- 4730 if (CharSequence.class.isAssignableFrom(inType) && outType.isEnum()) { 4731 return (in, memberOf, session, args) -> { 4732 try { 4733 return toMeta.newInstanceFromString(memberOf, in.toString()); 4734 } catch (Exception e) { 4735 throw rex(e); 4736 } 4737 }; 4738 } 4739 4740 // --- CharSequence → CharSequence subtype via ClassMeta (Gap 15) --- 4741 if (CharSequence.class.isAssignableFrom(inType) && toMeta.isCharSequence() && outType != String.class && toMeta.canCreateNewInstanceFromString(null)) { 4742 return (in, memberOf, session, args) -> { 4743 try { 4744 return toMeta.newInstanceFromString(memberOf, in.toString()); 4745 } catch (Exception e) { 4746 throw rex(e); 4747 } 4748 }; 4749 } 4750 4751 // --- Array → CharSequence subtype via serializer (Gap 15) --- 4752 if (inType.isArray() && toMeta.isCharSequence() && outType != String.class && toMeta.canCreateNewInstanceFromString(null)) { 4753 return (in, memberOf, session, args) -> { 4754 try { 4755 var ws = getBeanToStringSerializer(); 4756 var str = ws != null ? ws.write(in) : in.toString(); 4757 return toMeta.newInstanceFromString(memberOf, str); 4758 } catch (Exception e) { 4759 throw rex(e); 4760 } 4761 }; 4762 } 4763 4764 // --- Enum → T via toString() (allows Enum→Boolean, Enum→Number, etc.) --- 4765 // Excludes Enum→Enum (handled by BasicConverter) and Enum→String (handled by BasicConverter/CharSequence path). 4766 if (inType.isEnum() && !outType.isEnum() && !CharSequence.class.isAssignableFrom(outType)) { 4767 return (in, memberOf, session, args) -> converter.to(in.toString(), memberOf, session, outType, args); 4768 } 4769 4770 // --- Object → Boolean via toString() (lenient: any non-"true" string is false) --- 4771 // Excludes CharSequence types (handled by BasicConverter), primitives (handled by CachingConverter nullDefault), 4772 // and types that BasicConverter already handles (e.g. via toBoolean() instance method). 4773 if ((outType == Boolean.class || outType == boolean.class) && !CharSequence.class.isAssignableFrom(inType) && !Number.class.isAssignableFrom(inType) && inType != boolean.class && inType != Boolean.class && !BasicConverter.INSTANCE.canConvert(inType, Boolean.class)) { 4774 return (in, memberOf, session, args) -> converter.to(in.toString(), memberOf, session, Boolean.class); 4775 } 4776 4777 // --- Object → Bean via toString() + BeanMap.load() (fallback for bean-compatible types) --- 4778 // Replicates the old convertToMemberType fallback for beans (newBeanMap + load + getBean). 4779 // Excludes cases where input is already assignable to output (handled by MarshallingSession isInstance shortcut). 4780 if (toMeta.isBean() && !Map.class.isAssignableFrom(inType) && !CharSequence.class.isAssignableFrom(inType) 4781 && !outType.isAssignableFrom(inType)) { 4782 return (in, memberOf, session, args) -> { 4783 try { 4784 var bs = beanSessionOrDefault(session); 4785 return BeanMapLoader.load(bs.newBeanMap(toMeta.inner()), in.toString()).getBean(); 4786 } catch (Exception e) { 4787 throw rex(e); 4788 } 4789 }; 4790 } 4791 4792 return null; 4793 } 4794 4795 private static TimeZone sessionTimeZone(ConverterSession session) { 4796 return session == null ? null : session.get(TimeZone.class).orElse(null); 4797 } 4798 4799 private static MarshallingSession marshallingSession(ConverterSession session) { 4800 return session instanceof MarshallingSession session2 ? session2 : null; 4801 } 4802 4803 private MarshallingSession beanSessionOrDefault(ConverterSession session) { 4804 return session instanceof MarshallingSession session2 ? session2 : defaultSession; 4805 } 4806 4807 private static Collection<Object> newCollection(Class<?> outType) { 4808 if (outType == List.class || outType == Collection.class || outType == Iterable.class || outType == AbstractList.class) 4809 return l(); 4810 if (outType == Set.class || outType == LinkedHashSet.class || outType == AbstractSet.class) 4811 return new LinkedHashSet<>(); 4812 if (outType == SortedSet.class || outType == NavigableSet.class || outType == TreeSet.class) 4813 return new TreeSet<>(); 4814 try { 4815 return (Collection<Object>) outType.getDeclaredConstructor().newInstance(); 4816 } catch (@SuppressWarnings("unused") Exception e) { 4817 return l(); 4818 } 4819 } 4820 4821 /** 4822 * Returns the {@link BeanMeta} class for the specified class. 4823 * 4824 * @param <T> The class type to get the meta-data on. 4825 * @param c The class to get the meta-data on. 4826 * <br>Cannot be <jk>null</jk>. 4827 * @return 4828 * The {@link BeanMeta} for the specified class, or <jk>null</jk> if the class is not a bean per the settings on 4829 * this context. 4830 */ 4831 public final <T> BeanMeta<T> getBeanMeta(Class<T> c) { 4832 reqnn("c", c); 4833 return getClassMeta(c).getBeanMeta(); 4834 } 4835 4836 /** 4837 * Minimum bean method visibility. 4838 * 4839 * @see MarshallingContext.Builder#beanMethodVisibility(Visibility) 4840 * @return 4841 * Only look for bean methods with this specified minimum visibility. 4842 */ 4843 public final Visibility getBeanMethodVisibility() { return beanMethodVisibility; } 4844 4845 4846 /** 4847 * Bean type property name. 4848 * 4849 * @see MarshallingContext.Builder#typePropertyName(String) 4850 * @return 4851 * The name of the bean property used to store the dictionary name of a bean type so that the parser knows the data type to reconstruct. 4852 */ 4853 public final String getBeanTypePropertyName() { return typePropertyName; } 4854 4855 /** 4856 * Construct a {@code ClassMeta} wrapper around a {@link Class} object. 4857 * 4858 * @param <T> The class type being wrapped. 4859 * @param type The class to resolve. 4860 * <br>Cannot be <jk>null</jk>. 4861 * @return 4862 * If the class is not an array, returns a cached {@link ClassMeta} object. 4863 * Otherwise, returns a new {@link ClassMeta} object every time. 4864 */ 4865 public final <T> ClassMeta<T> getClassMeta(Class<T> type) { 4866 // Non-cacheable classes (proxies, generated classes) should not be cached 4867 if (! isCacheable(type)) 4868 return new ClassMeta<>(type, this); 4869 return cmCache.get(type); 4870 } 4871 4872 /** 4873 * Used to resolve <c>ClassMetas</c> of type <c>Collection</c> and <c>Map</c> that have 4874 * <c>ClassMeta</c> values that themselves could be collections or maps. 4875 * 4876 * <p> 4877 * <c>Collection</c> meta objects are assumed to be followed by zero or one meta objects indicating the element type. 4878 * 4879 * <p> 4880 * <c>Map</c> meta objects are assumed to be followed by zero or two meta objects indicating the key and value types. 4881 * 4882 * <p> 4883 * The array can be arbitrarily long to indicate arbitrarily complex data structures. 4884 * 4885 * <h5 class='section'>Examples:</h5> 4886 * <ul> 4887 * <li><code>getClassMeta(String.<jk>class</jk>);</code> - A normal type. 4888 * <li><code>getClassMeta(List.<jk>class</jk>);</code> - A list containing objects. 4889 * <li><code>getClassMeta(List.<jk>class</jk>, String.<jk>class</jk>);</code> - A list containing strings. 4890 * <li><code>getClassMeta(LinkedList.<jk>class</jk>, String.<jk>class</jk>);</code> - A linked-list containing 4891 * strings. 4892 * <li><code>getClassMeta(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);</code> - 4893 * A linked-list containing linked-lists of strings. 4894 * <li><code>getClassMeta(Map.<jk>class</jk>);</code> - A map containing object keys/values. 4895 * <li><code>getClassMeta(Map.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);</code> - A map 4896 * containing string keys/values. 4897 * <li><code>getClassMeta(Map.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);</code> - 4898 * A map containing string keys and values of lists containing beans. 4899 * </ul> 4900 * 4901 * @param <T> 4902 * The class to resolve. 4903 * @param type 4904 * The class to resolve. 4905 * <br>Can be any of the following: {@link ClassMeta}, {@link ClassInfo}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 4906 * <br>Can be <jk>null</jk> (returns <jk>null</jk>). 4907 * @param args 4908 * The type arguments of the class if it's a collection or map. 4909 * <br>Can be any of the following: {@link ClassMeta}, {@link ClassInfo}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 4910 * <br>Ignored if the main type is not a map or collection. 4911 * @return The resolved class meta, or <jk>null</jk> if the type was <jk>null</jk>. 4912 */ 4913 public final <T> ClassMeta<T> getClassMeta(Type type, Type...args) { 4914 if (type == null) 4915 return null; 4916 ClassMeta<T> cm = null; 4917 if (type instanceof Class type2) 4918 cm = getClassMeta(type2); 4919 else 4920 cm = resolveClassMeta(type, null); 4921 if (args.length == 0) 4922 return cm; 4923 var cma = new ClassMeta<?>[args.length + 1]; 4924 cma[0] = cm; 4925 for (var i = 0; i < length(args); i++) { 4926 var arg = (Type)Array.get(args, i); 4927 if (arg instanceof Class arg2) 4928 cma[i + 1] = getClassMeta(arg2); 4929 else 4930 cma[i + 1] = resolveClassMeta(arg, null); 4931 } 4932 return (ClassMeta<T>)getTypedClassMeta(cma, 0); 4933 } 4934 4935 /** 4936 * Returns the {@link ClassMeta} for the object's class, unwrapping recognized proxies first. 4937 * 4938 * <p> 4939 * Same contract as {@link MarshallingSession#getClassMetaForObject(Object, ClassMeta)}: 4940 * {@link org.apache.juneau.commons.utils.ClassUtils#getProxyFor(Object)} if non-<jk>null</jk>, 4941 * else {@code o.getClass()}. Used by object-tools and other context callers; parsers do not 4942 * call this method. 4943 * 4944 * @param <T> The class of the object being passed in. 4945 * @param o The object to find the class type for. 4946 * <br>Cannot be <jk>null</jk>. 4947 * @return The ClassMeta object. 4948 */ 4949 public final <T> ClassMeta<T> getClassMetaForObject(T o) { 4950 reqnn("o", o); 4951 var unwrapped = getProxyFor(o); 4952 Class<?> k = unwrapped != null ? unwrapped : o.getClass(); 4953 return (ClassMeta<T>)getClassMeta(k); 4954 } 4955 4956 /** 4957 * Locale. 4958 * 4959 * @see MarshallingContext.Builder#locale(Locale) 4960 * @return 4961 * The default locale for serializer and parser sessions. 4962 */ 4963 public final Locale getDefaultLocale() { return locale; } 4964 4965 /** 4966 * Media type. 4967 * 4968 * @see MarshallingContext.Builder#mediaType(MediaType) 4969 * @return 4970 * The default media type value for serializer and parser sessions. 4971 * <br>Can be <jk>null</jk> if no default media type has been configured. 4972 */ 4973 public final MediaType getDefaultMediaType() { return mediaType; } 4974 4975 /** 4976 * Time zone. 4977 * 4978 * @see MarshallingContext.Builder#timeZone(TimeZone) 4979 * @return 4980 * The default timezone for serializer and parser sessions. 4981 * <br>Can be <jk>null</jk> if no default time zone has been configured. 4982 */ 4983 public final TimeZone getDefaultTimeZone() { return timeZone; } 4984 4985 /** 4986 * Bean package exclusions. 4987 * 4988 * @see MarshallingContext.Builder#notBeanPackages(String...) 4989 * @return 4990 * The set of fully-qualified package names to exclude from being classified as beans. 4991 * <br>Never <jk>null</jk>. 4992 * <br>Set is unmodifiable. 4993 * <br>Backed by a {@link LinkedHashSet} to preserve insertion order. 4994 */ 4995 public final Set<String> getNotBeanPackagesNames() { return notBeanPackageNames; } 4996 4997 /** 4998 * Bean property namer. 4999 * 5000 * @see MarshallingContext.Builder#propertyNamer(Class) 5001 * @return 5002 * The interface used to calculate bean property names. 5003 */ 5004 public final PropertyNamer getPropertyNamer() { return propertyNamerBean; } 5005 5006 @Override /* Overridden from Context */ 5007 public MarshallingSession getSession() { return defaultSession; } 5008 5009 /** 5010 * Java object swaps. 5011 * 5012 * @see MarshallingContext.Builder#swaps(Class...) 5013 * @return 5014 * The list POJO swaps defined. 5015 * <br>Never <jk>null</jk>. 5016 * <br>List is unmodifiable. 5017 */ 5018 public final List<ObjectSwap<?,?>> getSwaps() { return objectSwaps; } 5019 5020 /** 5021 * Returns <jk>true</jk> if the specified bean context shares the same cache as this bean context. 5022 * 5023 * <p> 5024 * Useful for testing purposes. 5025 * 5026 * @param bc The bean context to compare to. 5027 * <br>Cannot be <jk>null</jk>. 5028 * @return <jk>true</jk> if the bean contexts have equivalent settings and thus share caches. 5029 */ 5030 public final boolean hasSameCache(MarshallingContext bc) { 5031 reqnn("bc", bc); 5032 return bc.getCmCache() == this.getCmCache(); 5033 } 5034 5035 /** 5036 * BeanMap.put() returns old property value. 5037 * 5038 * @see MarshallingContext.Builder#beanMapPutReturnsOldValue() 5039 * @return 5040 * <jk>true</jk> if the {@link BeanMap#put(String,Object) BeanMap.put()} method will return old property values. 5041 * <br>Otherwise, it returns <jk>null</jk>. 5042 */ 5043 public final boolean isBeanMapPutReturnsOldValue() { return beanMapPutReturnsOldValue; } 5044 5045 /** 5046 * Beans require no-arg constructors. 5047 * 5048 * @see MarshallingContext.Builder#beansRequireDefaultConstructor() 5049 * @return 5050 * <jk>true</jk> if a Java class must implement a default no-arg constructor to be considered a bean. 5051 * <br>Otherwise, the bean will be serialized as a string using the {@link Object#toString()} method. 5052 */ 5053 public final boolean isBeansRequireDefaultConstructor() { return beansRequireDefaultConstructor; } 5054 5055 /** 5056 * Beans require Serializable interface. 5057 * 5058 * @see MarshallingContext.Builder#beansRequireSerializable() 5059 * @return 5060 * <jk>true</jk> if a Java class must implement the {@link Serializable} interface to be considered a bean. 5061 * <br>Otherwise, the bean will be serialized as a string using the {@link Object#toString()} method. 5062 */ 5063 public final boolean isBeansRequireSerializable() { return beansRequireSerializable; } 5064 5065 /** 5066 * Beans require setters for getters. 5067 * 5068 * @see MarshallingContext.Builder#beansRequireSettersForGetters() 5069 * @return 5070 * <jk>true</jk> if only getters that have equivalent setters will be considered as properties on a bean. 5071 * <br>Otherwise, they are ignored. 5072 */ 5073 public final boolean isBeansRequireSettersForGetters() { return beansRequireSettersForGetters; } 5074 5075 /** 5076 * Beans require at least one property. 5077 * 5078 * @see MarshallingContext.Builder#disableBeansRequireSomeProperties() 5079 * @return 5080 * <jk>true</jk> if a Java class doesn't need to contain at least 1 property to be considered a bean. 5081 * <br>Otherwise, the bean is serialized as a string using the {@link Object#toString()} method. 5082 */ 5083 public final boolean isBeansRequireSomeProperties() { return beansRequireSomeProperties; } 5084 5085 /** 5086 * Find fluent setters. 5087 * 5088 * <h5 class='section'>Description:</h5> 5089 * <p> 5090 * 5091 * @see MarshallingContext.Builder#findFluentSetters() 5092 * @return 5093 * <jk>true</jk> if fluent setters are detected on beans. 5094 */ 5095 public final boolean isFindFluentSetters() { return findFluentSetters; } 5096 5097 /** 5098 * Ignore invocation errors on getters. 5099 * 5100 * @see MarshallingContext.Builder#ignoreInvocationExceptionsOnGetters() 5101 * @return 5102 * <jk>true</jk> if errors thrown when calling bean getter methods are silently ignored. 5103 */ 5104 public final boolean isIgnoreInvocationExceptionsOnGetters() { return ignoreInvocationExceptionsOnGetters; } 5105 5106 /** 5107 * Ignore invocation errors on setters. 5108 * 5109 * @see MarshallingContext.Builder#ignoreInvocationExceptionsOnSetters() 5110 * @return 5111 * <jk>true</jk> if errors thrown when calling bean setter methods are silently ignored. 5112 */ 5113 public final boolean isIgnoreInvocationExceptionsOnSetters() { return ignoreInvocationExceptionsOnSetters; } 5114 5115 /** 5116 * Silently ignore missing setters. 5117 * 5118 * @see MarshallingContext.Builder#disableIgnoreMissingSetters() 5119 * @return 5120 * <jk>true</jk> if trying to set a value on a bean property without a setter should throw a {@link BeanRuntimeException}. 5121 */ 5122 public final boolean isIgnoreMissingSetters() { return ignoreMissingSetters; } 5123 5124 /** 5125 * Ignore unknown properties. 5126 * 5127 * @see MarshallingContext.Builder#ignoreUnknownBeanProperties() 5128 * @return 5129 * <jk>true</jk> if trying to set a value on a non-existent bean property is silently ignored. 5130 * <br>Otherwise, a {@code RuntimeException} is thrown. 5131 */ 5132 public final boolean isIgnoreUnknownBeanProperties() { return ignoreUnknownBeanProperties; } 5133 5134 /** 5135 * Schema validation mode. 5136 * 5137 * @see MarshallingContext.Builder#validateSchema() 5138 * @return 5139 * <jk>true</jk> if bean property values should be validated against their {@code @Schema}-declared constraints 5140 * during parsing and serialization. 5141 * @since 10.0.0 5142 */ 5143 public final boolean isValidateSchema() { return validateSchema; } 5144 5145 /** 5146 * Ignore unknown enum values. 5147 * 5148 * @see MarshallingContext.Builder#ignoreUnknownEnumValues() 5149 * @return 5150 * <jk>true</jk> if unknown enum values should be set as <jk>null</jk> instead of throwing an exception. 5151 */ 5152 public final boolean isIgnoreUnknownEnumValues() { return ignoreUnknownEnumValues; } 5153 5154 /** 5155 * Ignore unknown properties with null values. 5156 * 5157 * @see MarshallingContext.Builder#disableIgnoreUnknownNullBeanProperties() 5158 * @return 5159 * <jk>true</jk> if trying to set a <jk>null</jk> value on a non-existent bean property should throw a {@link BeanRuntimeException}. 5160 */ 5161 public final boolean isIgnoreUnknownNullBeanProperties() { return ignoreUnknownNullBeanProperties; } 5162 5163 /** 5164 * Returns whether bean properties are unsorted (i.e. in declaration order rather than alphabetical). 5165 * 5166 * @see MarshallingContext.Builder#unsortedProperties() 5167 * @return 5168 * <jk>true</jk> if bean properties are in declaration order; <jk>false</jk> (the default) means alphabetical order. 5169 */ 5170 public final boolean isUnsortedProperties() { return unsortedProperties; } 5171 5172 /** 5173 * Returns whether {@link Record} beans use record component order. 5174 * 5175 * @see MarshallingContext.Builder#recordComponentOrder() 5176 * @return <jk>true</jk> if record properties are in component order regardless of {@link #isUnsortedProperties()}. 5177 */ 5178 public final boolean isRecordComponentOrder() { return recordComponentOrder; } 5179 5180 /** 5181 * Use interface proxies. 5182 * 5183 * @see MarshallingContext.Builder#disableInterfaceProxies() 5184 * @return 5185 * <jk>true</jk> if interfaces will be instantiated as proxy classes through the use of an 5186 * {@link InvocationHandler} if there is no other way of instantiating them. 5187 */ 5188 public final boolean isUseInterfaceProxies() { return useInterfaceProxies; } 5189 5190 /** 5191 * Use Java Introspector. 5192 * 5193 * @see MarshallingContext.Builder#useJavaBeanIntrospector() 5194 * @return 5195 * <jk>true</jk> if the built-in Java bean introspector should be used for bean introspection. 5196 */ 5197 public final boolean isUseJavaBeanIntrospector() { return useJavaBeanIntrospector; } 5198 5199 /** 5200 * Creates a new {@link BeanMap} object (a modifiable {@link Map}) of the given class with uninitialized 5201 * property values. 5202 * 5203 * <p> 5204 * This is a shortcut for the following code: <c>createSession().build().newBeanMap(<jv>_class</jv>);</c> 5205 * 5206 * <h5 class='section'>Example:</h5> 5207 * <p class='bjava'> 5208 * <jc>// Construct a new bean map wrapped around a new Person object</jc> 5209 * BeanMap<Person> <jv>beanMap</jv> = MarshallingContext.<jsf>DEFAULT</jsf>.newBeanMap(Person.<jk>class</jk>); 5210 * </p> 5211 * 5212 * @param <T> The class of the object being wrapped. 5213 * @param c The name of the class to create a new instance of. 5214 * <br>Cannot be <jk>null</jk>. 5215 * @return A new instance of the class, or <jk>null</jk> if the class is not a bean. 5216 * @see MarshallingSession#newBeanMap(Class) 5217 */ 5218 public <T> BeanMap<T> newBeanMap(Class<T> c) { 5219 reqnn("c", c); 5220 return defaultSession.newBeanMap(c); 5221 } 5222 5223 /** 5224 * Wraps an object inside a {@link BeanMap} object (a modifiable {@link Map}). 5225 * 5226 * <p> 5227 * This is a shortcut for the following code: <c>createSession().build().toBeanMap(<jv>object</jv>);</c> 5228 * 5229 * <h5 class='section'>Example:</h5> 5230 * <p class='bjava'> 5231 * <jc>// Construct a bean map around a bean instance</jc> 5232 * BeanMap<Person> <jv>beanMap</jv> = MarshallingContext.<jsf>DEFAULT</jsf>.toBeanMap(<jk>new</jk> Person()); 5233 * </p> 5234 * 5235 * @param <T> The class of the object being wrapped. 5236 * @param object The object to wrap in a map interface. 5237 * <br>Cannot be <jk>null</jk>. 5238 * @return The wrapped object. 5239 * @see MarshallingSession#toBeanMap(Object) 5240 */ 5241 public <T> BeanMap<T> toBeanMap(T object) { 5242 reqnn("object", object); 5243 return defaultSession.toBeanMap(object); 5244 } 5245 5246 /** 5247 * Returns the lookup table for resolving bean types by name. 5248 * 5249 * @return The lookup table for resolving bean types by name. 5250 */ 5251 protected final BeanRegistry getBeanRegistry() { return beanRegistry; } 5252 5253 /** 5254 * Returns the serializer to use for serializing beans when using the {@link MarshallingSession#convertToType(Object, Class)} 5255 * and related methods. 5256 * 5257 * @return The serializer. May be <jk>null</jk> if all initialization has occurred. 5258 */ 5259 protected WriterSerializer getBeanToStringSerializer() { 5260 return beanToStringSerializer.get(); 5261 } 5262 5263 /** 5264 * Class metadata cache. 5265 * 5266 * @return The class metadata cache. 5267 */ 5268 protected final Cache<Class,ClassMeta> getCmCache() { return cmCache; } 5269 5270 5271 /** 5272 * Hash key. 5273 * 5274 * @return The hash key. 5275 */ 5276 protected final HashKey getHashKey() { return hashKey; } 5277 5278 /** 5279 * Locale. 5280 * 5281 * @return The locale. 5282 */ 5283 protected final Locale getLocale() { return locale; } 5284 5285 /** 5286 * The default active view for sessions created from this context. 5287 * 5288 * <p> 5289 * Returns the view name set via {@link Builder#activeView(String)}, or <jk>null</jk> if no default 5290 * active view has been configured (all properties visible). 5291 * 5292 * @return The default active view, or <jk>null</jk>. 5293 * @since 10.0.0 5294 */ 5295 public final String getActiveView() { return activeView; } 5296 5297 /** 5298 * Default-view-inclusion policy. 5299 * 5300 * <p> 5301 * Returns <jk>true</jk> (the default) when untagged properties — those carrying no 5302 * {@link MarshalledProp#view()} declaration — are included under every active view. 5303 * Returns <jk>false</jk> when {@link Builder#disableDefaultViewInclusion()} has been called, 5304 * in which case untagged properties are excluded when any active view is in effect. 5305 * 5306 * @return <jk>true</jk> if untagged properties are included under every active view. 5307 * @since 10.0.0 5308 */ 5309 public final boolean isDefaultViewInclusion() { return defaultViewInclusion; } 5310 5311 /** 5312 * Media type. 5313 * 5314 * @return The media type, or <jk>null</jk> if no default media type has been configured. 5315 */ 5316 protected final MediaType getMediaType() { return mediaType; } 5317 5318 /** 5319 * Bean class exclusions. 5320 * 5321 * @see MarshallingContext.Builder#notBeanClasses(ClassInfo...) 5322 * @return 5323 * The list of classes that are explicitly not beans. 5324 * <br>Never <jk>null</jk>. 5325 * <br>List is unmodifiable. 5326 */ 5327 protected final List<ClassInfo> getNotBeanClasses() { return notBeanClasses; } 5328 5329 /** 5330 * Time zone. 5331 * 5332 * @return The time zone, or <jk>null</jk> if no default time zone has been configured. 5333 */ 5334 protected final TimeZone getTimeZone() { return timeZone; } 5335 5336 /** 5337 * Duration wire format. 5338 * 5339 * @return The duration wire format. 5340 */ 5341 public final DurationFormat getDurationFormat() { return durationFormat; } 5342 5343 /** 5344 * Period wire format. 5345 * 5346 * @return The period wire format. 5347 */ 5348 public final PeriodFormat getPeriodFormat() { return periodFormat; } 5349 5350 /** 5351 * Calendar wire format. 5352 * 5353 * @return The calendar wire format. 5354 */ 5355 public final CalendarFormat getCalendarFormat() { return calendarFormat; } 5356 5357 /** 5358 * Date wire format. 5359 * 5360 * @return The date wire format. 5361 */ 5362 public final DateFormat getDateFormat() { return dateFormat; } 5363 5364 /** 5365 * Temporal wire format. 5366 * 5367 * @return The temporal wire format. 5368 */ 5369 public final TemporalFormat getTemporalFormat() { return temporalFormat; } 5370 5371 /** 5372 * Time-zone wire format. 5373 * 5374 * @return The time-zone wire format. 5375 */ 5376 public final TimeZoneFormat getTimeZoneFormat() { return timeZoneFormat; } 5377 5378 /** 5379 * Locale wire format. 5380 * 5381 * @return The locale wire format. 5382 */ 5383 public final LocaleFormat getLocaleFormat() { return localeFormat; } 5384 5385 /** 5386 * Binary wire format. 5387 * 5388 * @return The binary wire format. 5389 */ 5390 public final BinaryFormat getBinaryFormat() { return binaryFormat; } 5391 5392 /** 5393 * Enum wire format. 5394 * 5395 * @return The enum wire format. 5396 */ 5397 public final EnumFormat getEnumFormat() { return enumFormat; } 5398 5399 /** 5400 * UUID wire format. 5401 * 5402 * @return The UUID wire format. 5403 */ 5404 public final UuidFormat getUuidFormat() { return uuidFormat; } 5405 5406 /** 5407 * BitSet wire format. 5408 * 5409 * @return The BitSet wire format. 5410 */ 5411 public final BitSetFormat getBitSetFormat() { return bitSetFormat; } 5412 5413 /** 5414 * Big-number wire format. 5415 * 5416 * @return The big-number wire format. 5417 */ 5418 public final BigNumberFormat getBigNumberFormat() { return bigNumberFormat; } 5419 5420 /** 5421 * Boolean wire format. 5422 * 5423 * @return The boolean wire format. 5424 */ 5425 public final BooleanFormat getBooleanFormat() { return booleanFormat; } 5426 5427 /** 5428 * Float / Double non-finite wire format. 5429 * 5430 * @return The float wire format. 5431 */ 5432 public final FloatFormat getFloatFormat() { return floatFormat; } 5433 5434 /** 5435 * Currency wire format. 5436 * 5437 * @return The currency wire format. 5438 */ 5439 public final CurrencyFormat getCurrencyFormat() { return currencyFormat; } 5440 5441 /** 5442 * Class wire format. 5443 * 5444 * @return The class wire format. 5445 */ 5446 public final ClassFormat getClassFormat() { return classFormat; } 5447 5448 /** 5449 * Session classloader. 5450 * 5451 * <p> 5452 * Returns the classloader explicitly configured via {@link Builder#classLoader(ClassLoader)}, or 5453 * <jk>null</jk> if none was set (callers should fall back to the thread-context classloader). 5454 * 5455 * @return The session classloader, or <jk>null</jk> if not set. 5456 */ 5457 public final ClassLoader getClassLoader() { return classLoader; } 5458 5459 /** 5460 * Ignore transient fields. 5461 * 5462 * @see MarshallingContext.Builder#disableIgnoreTransientFields() 5463 * @return 5464 * <jk>true</jk> if fields and methods marked as transient should not be ignored. 5465 */ 5466 protected final boolean isIgnoreTransientFields() { return ignoreTransientFields; } 5467 5468 /** 5469 * Returns a reusable {@link ClassMeta} representation for the class <c>Object</c>. 5470 * 5471 * <p> 5472 * This <c>ClassMeta</c> is often used to represent "any object type" when an object type is not known. 5473 * 5474 * <p> 5475 * This method is identical to calling <code>getClassMeta(Object.<jk>class</jk>)</code> but uses a cached copy to 5476 * avoid a hashmap lookup. 5477 * 5478 * @return The {@link ClassMeta} object associated with the <c>Object</c> class. 5479 */ 5480 protected final ClassMeta<Object> object() { 5481 return cmObject; 5482 } 5483 5484 //----------------------------------------------------------------------------------------------------------------- 5485 // BeanTypeResolver implementation — bridges the marshalling-side ClassMeta resolution into the bean-modeling 5486 // SPI seam that BeanPropertyMeta.Builder.validate() consumes. 5487 //----------------------------------------------------------------------------------------------------------------- 5488 5489 @Override /* Overridden from BeanTypeResolver */ 5490 public org.apache.juneau.commons.bean.BeanInfo<?> resolveType(AnnotationInfo<BeanProp> lastBeanProp, ClassInfo type, TypeVariables typeVarImpls) { 5491 return resolveClassMeta(lastBeanProp, type, typeVarImpls); 5492 } 5493 5494 @Override /* Overridden from BeanTypeResolver */ 5495 public org.apache.juneau.commons.bean.BeanInfo<?> objectType() { 5496 return cmObject; 5497 } 5498 5499 @Override /* Overridden from Context */ 5500 protected FluentMap<String,Object> properties() { 5501 return super.properties() 5502 .a("beanClassVisibility", beanClassVisibility) 5503 .a("beanConstructorVisibility", beanConstructorVisibility) 5504 .a("beanDictionary", beanDictionary) 5505 .a("beanFieldVisibility", beanFieldVisibility) 5506 .a("beanMethodVisibility", beanMethodVisibility) 5507 .a("beansRequireDefaultConstructor", beansRequireDefaultConstructor) 5508 .a("beansRequireSerializable", beansRequireSerializable) 5509 .a("beansRequireSettersForGetters", beansRequireSettersForGetters) 5510 .a("beansRequireSomeProperties", beansRequireSomeProperties) 5511 .a("id", System.identityHashCode(this)) 5512 .a("ignoreInvocationExceptionsOnGetters", ignoreInvocationExceptionsOnGetters) 5513 .a("ignoreInvocationExceptionsOnSetters", ignoreInvocationExceptionsOnSetters) 5514 .a("ignoreTransientFields", ignoreTransientFields) 5515 .a("ignoreUnknownBeanProperties", ignoreUnknownBeanProperties) 5516 .a("ignoreUnknownNullBeanProperties", ignoreUnknownNullBeanProperties) 5517 .a("notBeanClasses", notBeanClasses) 5518 .a("notBeanPackageNames", notBeanPackageNames) 5519 .a("notBeanPackagePrefixes", notBeanPackagePrefixes) 5520 .a("unsortedProperties", unsortedProperties) 5521 .a("recordComponentOrder", recordComponentOrder) 5522 .a("swaps", swaps) 5523 .a("durationFormat", durationFormat) 5524 .a("periodFormat", periodFormat) 5525 .a("calendarFormat", calendarFormat) 5526 .a("dateFormat", dateFormat) 5527 .a("temporalFormat", temporalFormat) 5528 .a("timeZoneFormat", timeZoneFormat) 5529 .a("localeFormat", localeFormat) 5530 .a("binaryFormat", binaryFormat) 5531 .a("enumFormat", enumFormat) 5532 .a("uuidFormat", uuidFormat) 5533 .a("bitSetFormat", bitSetFormat) 5534 .a("bigNumberFormat", bigNumberFormat) 5535 .a("booleanFormat", booleanFormat) 5536 .a("floatFormat", floatFormat) 5537 .a("currencyFormat", currencyFormat) 5538 .a("classFormat", classFormat) 5539 .a("classLoader", classLoader) 5540 .a("useInterfaceProxies", useInterfaceProxies) 5541 .a("useJavaBeanIntrospector", useJavaBeanIntrospector) 5542 .a("validateSchema", validateSchema); 5543 } 5544 5545 /** 5546 * Used for determining the class type on a method or field where a {@code @BeanProp} annotation may be present. 5547 * 5548 * @param <T> The class type we're wrapping. 5549 * @param p The property annotation info on the type if there is one. 5550 * <br>Can be <jk>null</jk> (the class type is resolved from <c>ci</c> alone, with no <ja>@BeanProp</ja>-based narrowing). 5551 * @param ci The class info for the type. 5552 * @param typeVarImpls 5553 * Contains known resolved type parameters on the specified class so that we can result 5554 * {@code ParameterizedTypes} and {@code TypeVariables}. 5555 * Can be <jk>null</jk> if the information is not known. 5556 * @return The new {@code ClassMeta} object wrapped around the type. 5557 */ 5558 protected final <T> ClassMeta<T> resolveClassMeta(AnnotationInfo<BeanProp> p, ClassInfo ci, TypeVariables typeVarImpls) { 5559 var cm = resolveClassMeta(ci, typeVarImpls); 5560 var cm2 = cm; 5561 5562 if (nn(p)) { 5563 var beanp = p.inner(); 5564 5565 if (isNotVoid(beanp.type())) 5566 cm2 = resolveClassMeta(beanp.type(), typeVarImpls); 5567 5568 if (cm2.isMap()) { 5569 var pParams = (beanp.params().length == 0 ? a(Object.class, Object.class) : beanp.params()); 5570 if (pParams.length != 2) 5571 throw rex("Invalid number of parameters specified for Map (must be 2): %s", pParams.length); 5572 var keyType = resolveType(pParams[0], cm2.getKeyType(), cm.getKeyType()); 5573 var valueType = resolveType(pParams[1], cm2.getValueType(), cm.getValueType()); 5574 if (keyType.isObject() && valueType.isObject()) 5575 return cm2; 5576 return new ClassMeta<>(cm2, keyType, valueType, null); 5577 } 5578 5579 if (cm2.isCollection() || cm2.isOptional()) { 5580 var pParams = (beanp.params().length == 0 ? a(Object.class) : beanp.params()); 5581 if (pParams.length != 1) { 5582 String typeName; 5583 if (cm2.isCollection()) { 5584 typeName = "Collection"; 5585 } else if (cm2.isOptional()) { 5586 typeName = "Optional"; 5587 } else { 5588 typeName = "Array"; 5589 } 5590 throw rex("Invalid number of parameters specified for %2$s (must be 1): %1$s", pParams.length, typeName); 5591 } 5592 var elementType = resolveType(pParams[0], cm2.getElementType(), cm.getElementType()); 5593 if (elementType.isObject()) 5594 return cm2; 5595 return new ClassMeta<>(cm2, null, null, elementType); 5596 } 5597 5598 // Handle @BeanProp(elementType=) for BeanSupplier, BeanConsumer, BeanChannel, and Stream properties. 5599 if (isNotVoid(beanp.elementType()) && (cm2.isIterable() || cm2.isStream())) { 5600 var elementType = resolveClassMeta(beanp.elementType(), typeVarImpls); 5601 if (! elementType.isObject()) 5602 return new ClassMeta<>(cm2, null, null, elementType); 5603 } 5604 5605 return cm2; 5606 } 5607 5608 return cm; 5609 } 5610 5611 /** 5612 * Returns a reusable {@link ClassMeta} representation for the class <c>String</c>. 5613 * 5614 * <p> 5615 * This <c>ClassMeta</c> is often used to represent key types in maps. 5616 * 5617 * <p> 5618 * This method is identical to calling <code>getClassMeta(String.<jk>class</jk>)</code> but uses a cached copy to 5619 * avoid a hashmap lookup. 5620 * 5621 * @return The {@link ClassMeta} object associated with the <c>String</c> class. 5622 */ 5623 protected final ClassMeta<String> string() { 5624 return cmString; 5625 } 5626 5627 /** 5628 * Finds the type parameters for a parameterized type. 5629 * 5630 * @param o The generic type. 5631 * @param c The raw class. 5632 * @return The array of {@link ClassMeta} for each type parameter, or an empty array if parameters cannot be resolved. 5633 */ 5634 final ClassMeta[] findParameters(Type o, Class c) { 5635 if (o == null) 5636 o = c; 5637 5638 // Loop until we find a ParameterizedType 5639 if (! (o instanceof ParameterizedType)) { 5640 while (nn(c)) { 5641 o = c.getGenericSuperclass(); 5642 if (!(o instanceof ParameterizedType)) 5643 o = Arrays.stream(c.getGenericInterfaces()).filter(t -> t instanceof ParameterizedType).findFirst().orElse(null); 5644 if (o instanceof ParameterizedType) 5645 break; 5646 c = c.getSuperclass(); 5647 } 5648 } 5649 5650 if (o instanceof ParameterizedType o2 && ! o2.getRawType().equals(Enum.class)) { 5651 var l = new LinkedList<ClassMeta<?>>(); 5652 for (var pt2 : o2.getActualTypeArguments()) { 5653 if (pt2 instanceof WildcardType || pt2 instanceof TypeVariable) 5654 return new ClassMeta[0]; 5655 l.add(resolveClassMeta(pt2, null)); 5656 } 5657 if (l.isEmpty()) 5658 return new ClassMeta[0]; 5659 return l.toArray(new ClassMeta[l.size()]); 5660 } 5661 5662 return new ClassMeta[0]; 5663 } 5664 5665 final ClassMeta resolveClassMeta(Type o, TypeVariables typeVars) { 5666 if (o == null) 5667 return null; 5668 5669 if (o instanceof ClassMeta<?> o2) { 5670 5671 // This classmeta could have been created by a different context. 5672 // Need to re-resolve it to pick up ObjectSwaps and stuff on this context. 5673 if (o2.getMarshallingContext() == this) 5674 return o2; 5675 if (o2.isMap()) 5676 return getClassMeta(o2.inner(), o2.getKeyType(), o2.getValueType()); 5677 if (o2.isCollection() || o2.isOptional()) 5678 return getClassMeta(o2.inner(), o2.getElementType()); 5679 return getClassMeta(o2.inner()); 5680 } 5681 5682 // Handle ClassInfo by extracting the underlying Type 5683 if (o instanceof ClassInfo o2) { 5684 return resolveClassMeta(o2.innerType(), typeVars); 5685 } 5686 5687 var c = TypeVariables.resolve(o, typeVars); 5688 5689 // This can happen when trying to resolve the "E getFirst()" method on LinkedList, whose type is a TypeVariable 5690 // These should just resolve to Object. 5691 if (c == null) 5692 return object(); 5693 5694 var rawType = getClassMeta(c); 5695 5696 // If this is a Map or Collection, and the parameter types aren't part 5697 // of the class definition itself (e.g. class AddressBook extends List<Person>), 5698 // then we need to figure out the parameters. 5699 if (rawType.isMap() || rawType.isCollection() || rawType.isOptional()) { 5700 var params = findParameters(o, c); 5701 if (params.length == 0) 5702 return rawType; 5703 if (rawType.isMap()) { 5704 if (params.length != 2 || (params[0].isObject() && params[1].isObject())) 5705 return rawType; 5706 return new ClassMeta(rawType, params[0], params[1], null); 5707 } 5708 if (rawType.isCollection() || rawType.isOptional()) { 5709 if (params.length != 1 || params[0].isObject()) 5710 return rawType; 5711 return new ClassMeta(rawType, null, null, params[0]); 5712 } 5713 } 5714 5715 if (rawType.isArray() && o instanceof GenericArrayType o2) { 5716 var elementType = resolveClassMeta(o2.getGenericComponentType(), typeVars); 5717 return new ClassMeta(rawType, null, null, elementType); 5718 } 5719 5720 return rawType; 5721 } 5722 5723 /* 5724 * Resolves the 'genericized' class meta at the specified position in the ClassMeta array. 5725 */ 5726 private ClassMeta<?> getTypedClassMeta(ClassMeta<?>[] c, int pos) { 5727 var cm = c[pos++]; 5728 if (cm.isCollection() || cm.isOptional()) { 5729 var ce = c.length == pos ? object() : getTypedClassMeta(c, pos); 5730 return (ce.isObject() ? cm : new ClassMeta(cm, null, null, ce)); 5731 } else if (cm.isMap()) { 5732 var ck = c.length == pos ? object() : c[pos++]; 5733 var cv = c.length == pos ? object() : getTypedClassMeta(c, pos); 5734 return (ck.isObject() && cv.isObject() ? cm : new ClassMeta(cm, ck, cv, null)); 5735 } 5736 return cm; 5737 } 5738 5739 5740 private ClassMeta<?> resolveType(Type...t) { 5741 for (var tt : t) { 5742 if (nn(tt)) { 5743 var cm = getClassMeta(tt); 5744 if (tt != cmObject) 5745 return cm; 5746 } 5747 } 5748 return cmObject; 5749 } 5750}