001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.serializer; 014 015import static org.apache.juneau.internal.ClassUtils.*; 016import static org.apache.juneau.internal.StringUtils.*; 017 018import java.io.*; 019import java.lang.reflect.*; 020import java.text.*; 021import java.util.*; 022 023import org.apache.juneau.*; 024import org.apache.juneau.internal.*; 025import org.apache.juneau.parser.*; 026import org.apache.juneau.soap.*; 027import org.apache.juneau.transform.*; 028 029/** 030 * Serializer session that lives for the duration of a single use of {@link Serializer}. 031 * 032 * <p> 033 * Used by serializers for the following purposes: 034 * <ul class='spaced-list'> 035 * <li> 036 * Keeping track of how deep it is in a model for indentation purposes. 037 * <li> 038 * Ensuring infinite loops don't occur by setting a limit on how deep to traverse a model. 039 * <li> 040 * Ensuring infinite loops don't occur from loops in the model (when detectRecursions is enabled. 041 * <li> 042 * Allowing serializer properties to be overridden on method calls. 043 * </ul> 044 * 045 * <p> 046 * This class is NOT thread safe. 047 * It is typically discarded after one-time use although it can be reused within the same thread. 048 */ 049public abstract class SerializerSession extends BeanTraverseSession { 050 051 private final Serializer ctx; 052 private final UriResolver uriResolver; 053 054 private final Method javaMethod; // Java method that invoked this serializer. 055 056 // Writable properties 057 private final boolean useWhitespace; 058 private final SerializerListener listener; 059 060 /** 061 * Create a new session using properties specified in the context. 062 * 063 * @param ctx 064 * The context creating this session object. 065 * The context contains all the configuration settings for this object. 066 * Can be <jk>null</jk>. 067 * @param args 068 * Runtime arguments. 069 * These specify session-level information such as locale and URI context. 070 * It also include session-level properties that override the properties defined on the bean and 071 * serializer contexts. 072 */ 073 protected SerializerSession(Serializer ctx, SerializerSessionArgs args) { 074 super(ctx, args == null ? SerializerSessionArgs.DEFAULT : args); 075 this.ctx = ctx; 076 args = args == null ? SerializerSessionArgs.DEFAULT : args; 077 this.javaMethod = args.javaMethod; 078 this.uriResolver = new UriResolver(ctx.getUriResolution(), ctx.getUriRelativity(), args.uriContext == null ? ctx.getUriContext() : args.uriContext); 079 this.listener = newInstance(SerializerListener.class, ctx.getListener()); 080 this.useWhitespace = args.useWhitespace != null ? args.useWhitespace : ctx.isUseWhitespace(); 081 } 082 083 /** 084 * Default constructor. 085 * 086 * @param args 087 * Runtime arguments. 088 * These specify session-level information such as locale and URI context. 089 * It also include session-level properties that override the properties defined on the bean and 090 * serializer contexts. 091 */ 092 protected SerializerSession(SerializerSessionArgs args) { 093 this(Serializer.DEFAULT, args); 094 } 095 096 @Override /* Session */ 097 public ObjectMap asMap() { 098 return super.asMap() 099 .append("SerializerSession", new ObjectMap() 100 .append("uriResolver", uriResolver) 101 ); 102 } 103 104 /** 105 * Wraps the specified input object into a {@link ParserPipe} object so that it can be easily converted into 106 * a stream or reader. 107 * 108 * @param output 109 * The output location. 110 * <br>For character-based serializers, this can be any of the following types: 111 * <ul> 112 * <li>{@link Writer} 113 * <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream. 114 * <li>{@link File} - Output will be written as system-default encoded stream. 115 * <li>{@link StringBuilder} 116 * </ul> 117 * <br>For byte-based serializers, this can be any of the following types: 118 * <ul> 119 * <li>{@link OutputStream} 120 * <li>{@link File} 121 * </ul> 122 * @return 123 * A new {@link ParserPipe} wrapper around the specified input object. 124 */ 125 protected SerializerPipe createPipe(Object output) { 126 return new SerializerPipe(output); 127 } 128 129 130 //----------------------------------------------------------------------------------------------------------------- 131 // Abstract methods 132 //----------------------------------------------------------------------------------------------------------------- 133 134 /** 135 * Serializes a POJO to the specified output stream or writer. 136 * 137 * <p> 138 * This method should NOT close the context object. 139 * 140 * @param pipe Where to send the output from the serializer. 141 * @param o The object to serialize. 142 * @throws Exception If thrown from underlying stream, or if the input contains a syntax error or is malformed. 143 */ 144 protected abstract void doSerialize(SerializerPipe pipe, Object o) throws Exception; 145 146 /** 147 * Shortcut method for serializing objects directly to either a <code>String</code> or <code><jk>byte</jk>[]</code> 148 * depending on the serializer type. 149 * 150 * @param o The object to serialize. 151 * @return 152 * The serialized object. 153 * <br>Character-based serializers will return a <code>String</code>. 154 * <br>Stream-based serializers will return a <code><jk>byte</jk>[]</code>. 155 * @throws SerializeException If a problem occurred trying to convert the output. 156 */ 157 public abstract Object serialize(Object o) throws SerializeException; 158 159 /** 160 * Shortcut method for serializing an object to a String. 161 * 162 * @param o The object to serialize. 163 * @return 164 * The serialized object. 165 * <br>Character-based serializers will return a <code>String</code> 166 * <br>Stream-based serializers will return a <code><jk>byte</jk>[]</code> converted to a string based on the {@link OutputStreamSerializer#OSSERIALIZER_binaryFormat} setting. 167 * @throws SerializeException If a problem occurred trying to convert the output. 168 */ 169 public abstract String serializeToString(Object o) throws SerializeException; 170 171 /** 172 * Returns <jk>true</jk> if this serializer subclasses from {@link WriterSerializer}. 173 * 174 * @return <jk>true</jk> if this serializer subclasses from {@link WriterSerializer}. 175 */ 176 public abstract boolean isWriterSerializer(); 177 178 179 //----------------------------------------------------------------------------------------------------------------- 180 // Other methods 181 //----------------------------------------------------------------------------------------------------------------- 182 183 /** 184 * Serialize the specified object using the specified session. 185 * 186 * @param out Where to send the output from the serializer. 187 * @param o The object to serialize. 188 * @throws SerializeException If a problem occurred trying to convert the output. 189 */ 190 public final void serialize(Object o, Object out) throws SerializeException { 191 try (SerializerPipe pipe = createPipe(out)) { 192 doSerialize(pipe, o); 193 } catch (SerializeException e) { 194 throw e; 195 } catch (StackOverflowError e) { 196 throw new SerializeException(this, 197 "Stack overflow occurred. This can occur when trying to serialize models containing loops. It's recommended you use the BeanTraverseContext.BEANTRAVERSE_detectRecursions setting to help locate the loop.").initCause(e); 198 } catch (Exception e) { 199 throw new SerializeException(this, e); 200 } finally { 201 checkForWarnings(); 202 } 203 } 204 205 /** 206 * Returns the Java method that invoked this serializer. 207 * 208 * <p> 209 * When using the REST API, this is the Java method invoked by the REST call. 210 * Can be used to access annotations defined on the method or class. 211 * 212 * @return The Java method that invoked this serializer. 213 */ 214 protected final Method getJavaMethod() { 215 return javaMethod; 216 } 217 218 /** 219 * Returns the URI resolver. 220 * 221 * @return The URI resolver. 222 */ 223 protected final UriResolver getUriResolver() { 224 return uriResolver; 225 } 226 227 /** 228 * Specialized warning when an exception is thrown while executing a bean getter. 229 * 230 * @param p The bean map entry representing the bean property. 231 * @param t The throwable that the bean getter threw. 232 */ 233 protected final void onBeanGetterException(BeanPropertyMeta p, Throwable t) { 234 if (listener != null) 235 listener.onBeanGetterException(this, t, p); 236 String prefix = (isDebug() ? getStack(false) + ": " : ""); 237 addWarning("{0}Could not call getValue() on property ''{1}'' of class ''{2}'', exception = {3}", prefix, 238 p.getName(), p.getBeanMeta().getClassMeta(), t.getLocalizedMessage()); 239 } 240 241 /** 242 * Logs a warning message. 243 * 244 * @param t The throwable that was thrown (if there was one). 245 * @param msg The warning message. 246 * @param args Optional {@link MessageFormat}-style arguments. 247 */ 248 @Override 249 protected void onError(Throwable t, String msg, Object... args) { 250 if (listener != null) 251 listener.onError(this, t, format(msg, args)); 252 super.onError(t, msg, args); 253 } 254 255 /** 256 * Trims the specified string if {@link SerializerSession#isTrimStrings()} returns <jk>true</jk>. 257 * 258 * @param o The input string to trim. 259 * @return The trimmed string, or <jk>null</jk> if the input was <jk>null</jk>. 260 */ 261 public final String trim(Object o) { 262 if (o == null) 263 return null; 264 String s = o.toString(); 265 if (isTrimStrings()) 266 s = s.trim(); 267 return s; 268 } 269 270 /** 271 * Generalize the specified object if a POJO swap is associated with it. 272 * 273 * @param o The object to generalize. 274 * @param type The type of object. 275 * @return The generalized object, or <jk>null</jk> if the object is <jk>null</jk>. 276 * @throws SerializeException If a problem occurred trying to convert the output. 277 */ 278 @SuppressWarnings({ "rawtypes", "unchecked" }) 279 protected final Object generalize(Object o, ClassMeta<?> type) throws SerializeException { 280 try { 281 if (o == null) 282 return null; 283 PojoSwap f = (type == null || type.isObject() ? getClassMeta(o.getClass()).getPojoSwap(this) : type.getPojoSwap(this)); 284 if (f == null) 285 return o; 286 return f.swap(this, o); 287 } catch (SerializeException e) { 288 throw e; 289 } catch (Exception e) { 290 throw new SerializeException(e); 291 } 292 } 293 294 /** 295 * Returns <jk>true</jk> if the specified value should not be serialized. 296 * 297 * @param cm The class type of the object being serialized. 298 * @param attrName The bean attribute name, or <jk>null</jk> if this isn't a bean attribute. 299 * @param value The object being serialized. 300 * @return <jk>true</jk> if the specified value should not be serialized. 301 * @throws SerializeException If recursion occurred. 302 */ 303 public final boolean canIgnoreValue(ClassMeta<?> cm, String attrName, Object value) throws SerializeException { 304 305 if (isTrimNullProperties() && value == null) 306 return true; 307 308 if (value == null) 309 return false; 310 311 if (cm == null) 312 cm = object(); 313 314 if (isTrimEmptyCollections()) { 315 if (cm.isArray() || (cm.isObject() && value.getClass().isArray())) { 316 if (((Object[])value).length == 0) 317 return true; 318 } 319 if (cm.isCollection() || (cm.isObject() && isParentClass(Collection.class, value.getClass()))) { 320 if (((Collection<?>)value).isEmpty()) 321 return true; 322 } 323 } 324 325 if (isTrimEmptyMaps()) { 326 if (cm.isMap() || (cm.isObject() && isParentClass(Map.class, value.getClass()))) { 327 if (((Map<?,?>)value).isEmpty()) 328 return true; 329 } 330 } 331 332 try { 333 if (isTrimNullProperties() && willRecurse(attrName, value, cm)) 334 return true; 335 } catch (BeanRecursionException e) { 336 throw new SerializeException(e); 337 } 338 339 return false; 340 } 341 342 /** 343 * Sorts the specified map if {@link SerializerSession#isSortMaps()} returns <jk>true</jk>. 344 * 345 * @param m The map being sorted. 346 * @return A new sorted {@link TreeMap}. 347 */ 348 public final <K,V> Map<K,V> sort(Map<K,V> m) { 349 if (isSortMaps() && m != null && (! m.isEmpty()) && m.keySet().iterator().next() instanceof Comparable<?>) 350 return new TreeMap<>(m); 351 return m; 352 } 353 354 /** 355 * Sorts the specified collection if {@link SerializerSession#isSortCollections()} returns <jk>true</jk>. 356 * 357 * @param c The collection being sorted. 358 * @return A new sorted {@link TreeSet}. 359 */ 360 public final <E> Collection<E> sort(Collection<E> c) { 361 if (isSortCollections() && c != null && (! c.isEmpty()) && c.iterator().next() instanceof Comparable<?>) 362 return new TreeSet<>(c); 363 return c; 364 } 365 366 /** 367 * Converts the contents of the specified object array to a list. 368 * 369 * <p> 370 * Works on both object and primitive arrays. 371 * 372 * <p> 373 * In the case of multi-dimensional arrays, the outgoing list will contain elements of type n-1 dimension. 374 * i.e. if {@code type} is <code><jk>int</jk>[][]</code> then {@code list} will have entries of type 375 * <code><jk>int</jk>[]</code>. 376 * 377 * @param type The type of array. 378 * @param array The array being converted. 379 * @return The array as a list. 380 */ 381 protected static final List<Object> toList(Class<?> type, Object array) { 382 Class<?> componentType = type.getComponentType(); 383 if (componentType.isPrimitive()) { 384 int l = Array.getLength(array); 385 List<Object> list = new ArrayList<>(l); 386 for (int i = 0; i < l; i++) 387 list.add(Array.get(array, i)); 388 return list; 389 } 390 return Arrays.asList((Object[])array); 391 } 392 393 /** 394 * Converts a String to an absolute URI based on the {@link UriContext} on this session. 395 * 396 * @param uri 397 * The input URI. 398 * Can be any of the following: 399 * <ul> 400 * <li>{@link java.net.URI} 401 * <li>{@link java.net.URL} 402 * <li>{@link CharSequence} 403 * </ul> 404 * URI can be any of the following forms: 405 * <ul> 406 * <li><js>"foo://foo"</js> - Absolute URI. 407 * <li><js>"/foo"</js> - Root-relative URI. 408 * <li><js>"/"</js> - Root URI. 409 * <li><js>"context:/foo"</js> - Context-root-relative URI. 410 * <li><js>"context:/"</js> - Context-root URI. 411 * <li><js>"servlet:/foo"</js> - Servlet-path-relative URI. 412 * <li><js>"servlet:/"</js> - Servlet-path URI. 413 * <li><js>"request:/foo"</js> - Request-path-relative URI. 414 * <li><js>"request:/"</js> - Request-path URI. 415 * <li><js>"foo"</js> - Path-info-relative URI. 416 * <li><js>""</js> - Path-info URI. 417 * </ul> 418 * @return The resolved URI. 419 */ 420 public final String resolveUri(Object uri) { 421 return uriResolver.resolve(uri); 422 } 423 424 /** 425 * Opposite of {@link #resolveUri(Object)}. 426 * 427 * <p> 428 * Converts the URI to a value relative to the specified <code>relativeTo</code> parameter. 429 * 430 * <p> 431 * Both parameters can be any of the following: 432 * <ul> 433 * <li>{@link java.net.URI} 434 * <li>{@link java.net.URL} 435 * <li>{@link CharSequence} 436 * </ul> 437 * 438 * <p> 439 * Both URIs can be any of the following forms: 440 * <ul> 441 * <li><js>"foo://foo"</js> - Absolute URI. 442 * <li><js>"/foo"</js> - Root-relative URI. 443 * <li><js>"/"</js> - Root URI. 444 * <li><js>"context:/foo"</js> - Context-root-relative URI. 445 * <li><js>"context:/"</js> - Context-root URI. 446 * <li><js>"servlet:/foo"</js> - Servlet-path-relative URI. 447 * <li><js>"servlet:/"</js> - Servlet-path URI. 448 * <li><js>"request:/foo"</js> - Request-path-relative URI. 449 * <li><js>"request:/"</js> - Request-path URI. 450 * <li><js>"foo"</js> - Path-info-relative URI. 451 * <li><js>""</js> - Path-info URI. 452 * </ul> 453 * 454 * @param relativeTo The URI to relativize against. 455 * @param uri The URI to relativize. 456 * @return The relativized URI. 457 */ 458 protected final String relativizeUri(Object relativeTo, Object uri) { 459 return uriResolver.relativize(relativeTo, uri); 460 } 461 462 /** 463 * Converts the specified object to a <code>String</code>. 464 * 465 * <p> 466 * Also has the following effects: 467 * <ul> 468 * <li><code>Class</code> object is converted to a readable name. See {@link ClassUtils#getReadableClassName(Class)}. 469 * <li>Whitespace is trimmed if the trim-strings setting is enabled. 470 * </ul> 471 * 472 * @param o The object to convert to a <code>String</code>. 473 * @return The object converted to a String, or <jk>null</jk> if the input was <jk>null</jk>. 474 */ 475 public final String toString(Object o) { 476 if (o == null) 477 return null; 478 if (o.getClass() == Class.class) 479 return getReadableClassName((Class<?>)o); 480 if (o.getClass().isEnum()) 481 return getClassMetaForObject(o).toString(o); 482 String s = o.toString(); 483 if (isTrimStrings()) 484 s = s.trim(); 485 return s; 486 } 487 488 /** 489 * Create a "_type" property that contains the dictionary name of the bean. 490 * 491 * @param m The bean map to create a class property on. 492 * @param typeName The type name of the bean. 493 * @return A new bean property value. 494 */ 495 protected static final BeanPropertyValue createBeanTypeNameProperty(BeanMap<?> m, String typeName) { 496 BeanMeta<?> bm = m.getMeta(); 497 return new BeanPropertyValue(bm.getTypeProperty(), bm.getTypeProperty().getName(), typeName, null); 498 } 499 500 /** 501 * Resolves the dictionary name for the actual type. 502 * 503 * @param eType The expected type of the bean property. 504 * @param aType The actual type of the bean property. 505 * @param pMeta The current bean property being serialized. 506 * @return The bean dictionary name, or <jk>null</jk> if a name could not be found. 507 */ 508 protected final String getBeanTypeName(ClassMeta<?> eType, ClassMeta<?> aType, BeanPropertyMeta pMeta) { 509 if (eType == aType) 510 return null; 511 512 if (! isAddBeanTypes()) 513 return null; 514 515 String eTypeTn = eType.getDictionaryName(); 516 517 // First see if it's defined on the actual type. 518 String tn = aType.getDictionaryName(); 519 if (tn != null && ! tn.equals(eTypeTn)) { 520 return tn; 521 } 522 523 // Then see if it's defined on the expected type. 524 // The expected type might be an interface with mappings for implementation classes. 525 BeanRegistry br = eType.getBeanRegistry(); 526 if (br != null) { 527 tn = br.getTypeName(aType); 528 if (tn != null && ! tn.equals(eTypeTn)) 529 return tn; 530 } 531 532 // Then look on the bean property. 533 br = pMeta == null ? null : pMeta.getBeanRegistry(); 534 if (br != null) { 535 tn = br.getTypeName(aType); 536 if (tn != null && ! tn.equals(eTypeTn)) 537 return tn; 538 } 539 540 // Finally look in the session. 541 br = getBeanRegistry(); 542 if (br != null) { 543 tn = br.getTypeName(aType); 544 if (tn != null && ! tn.equals(eTypeTn)) 545 return tn; 546 } 547 548 return null; 549 } 550 551 /** 552 * Returns the parser-side expected type for the object. 553 * 554 * <p> 555 * The return value depends on the {@link Serializer#SERIALIZER_addRootType} setting. 556 * When disabled, the parser already knows the Java POJO type being parsed, so there is 557 * no reason to add <js>"_type"</js> attributes to the root-level object. 558 * 559 * @param o The object to get the expected type on. 560 * @return The expected type. 561 */ 562 protected final ClassMeta<?> getExpectedRootType(Object o) { 563 return isAddRootType() ? object() : getClassMetaForObject(o); 564 } 565 566 /** 567 * Optional method that specifies HTTP request headers for this serializer. 568 * 569 * <p> 570 * For example, {@link SoapXmlSerializer} needs to set a <code>SOAPAction</code> header. 571 * 572 * <p> 573 * This method is typically meaningless if the serializer is being used stand-alone (i.e. outside of a REST server 574 * or client). 575 * 576 * @return 577 * The HTTP headers to set on HTTP requests. 578 * Never <jk>null</jk>. 579 */ 580 public Map<String,String> getResponseHeaders() { 581 return Collections.emptyMap(); 582 } 583 584 585 /** 586 * Returns the listener associated with this session. 587 * 588 * @return The listener associated with this session, or <jk>null</jk> if there is no listener. 589 */ 590 public SerializerListener getListener() { 591 return listener; 592 } 593 594 /** 595 * Returns the listener associated with this session. 596 * 597 * @param c The listener class to cast to. 598 * @return The listener associated with this session, or <jk>null</jk> if there is no listener. 599 */ 600 @SuppressWarnings("unchecked") 601 public <T extends SerializerListener> T getListener(Class<T> c) { 602 return (T)listener; 603 } 604 605 //----------------------------------------------------------------------------------------------------------------- 606 // Properties 607 //----------------------------------------------------------------------------------------------------------------- 608 609 /** 610 * Configuration property: Use whitespace. 611 * 612 * @see Serializer#SERIALIZER_useWhitespace 613 * @return 614 * <jk>true</jk> if whitespace is added to the output to improve readability. 615 */ 616 // TODO - Make final in 8.0 617 public boolean isUseWhitespace() { 618 return useWhitespace; 619 } 620 621 /** 622 * Configuration property: Add <js>"_type"</js> properties when needed. 623 * 624 * @see Serializer#SERIALIZER_addBeanTypes 625 * @return 626 * <jk>true</jk> if <js>"_type"</js> properties added to beans if their type cannot be inferred 627 * through reflection. 628 */ 629 protected boolean isAddBeanTypes() { 630 return ctx.isAddBeanTypes(); 631 } 632 633 /** 634 * Configuration property: Trim null bean property values. 635 * 636 * @see Serializer#SERIALIZER_trimNullProperties 637 * @return 638 * <jk>true</jk> if null bean values are not serialized to the output. 639 */ 640 protected final boolean isTrimNullProperties() { 641 return ctx.isTrimNullProperties(); 642 } 643 644 /** 645 * Configuration property: Trim empty lists and arrays. 646 * 647 * @see Serializer#SERIALIZER_trimEmptyCollections 648 * @return 649 * <jk>true</jk> if empty lists and arrays are not serialized to the output. 650 */ 651 protected final boolean isTrimEmptyCollections() { 652 return ctx.isTrimEmptyCollections(); 653 } 654 655 /** 656 * Configuration property: Trim empty maps. 657 * 658 * @see Serializer#SERIALIZER_trimEmptyMaps 659 * @return 660 * <jk>true</jk> if empty map values are not serialized to the output. 661 */ 662 protected final boolean isTrimEmptyMaps() { 663 return ctx.isTrimEmptyMaps(); 664 } 665 666 /** 667 * Configuration property: Trim strings. 668 * 669 * @see Serializer#SERIALIZER_trimStrings 670 * @return 671 * <jk>true</jk> if string values will be trimmed of whitespace using {@link String#trim()} before being serialized. 672 */ 673 protected boolean isTrimStrings() { 674 return ctx.isTrimStrings(); 675 } 676 677 /** 678 * Configuration property: Sort arrays and collections alphabetically. 679 * 680 * @see Serializer#SERIALIZER_sortCollections 681 * @return 682 * <jk>true</jk> if arrays and collections are copied and sorted before serialization. 683 */ 684 protected final boolean isSortCollections() { 685 return ctx.isSortCollections(); 686 } 687 688 /** 689 * Configuration property: Sort maps alphabetically. 690 * 691 * @see Serializer#SERIALIZER_sortMaps 692 * @return 693 * <jk>true</jk> if maps are copied and sorted before serialization. 694 */ 695 protected final boolean isSortMaps() { 696 return ctx.isSortMaps(); 697 } 698 699 /** 700 * Configuration property: Add type attribute to root nodes. 701 * 702 * @see Serializer#SERIALIZER_addRootType 703 * @return 704 * <jk>true</jk> if type property should be added to root node. 705 */ 706 protected final boolean isAddRootType() { 707 return ctx.isAddRootType(); 708 } 709 710 /** 711 * Configuration property: URI context bean. 712 * 713 * @see Serializer#SERIALIZER_uriContext 714 * @return 715 * Bean used for resolution of URIs to absolute or root-relative form. 716 */ 717 protected final UriContext getUriContext() { 718 return getUriContext(); 719 } 720 721 /** 722 * Configuration property: URI resolution. 723 * 724 * @see Serializer#SERIALIZER_uriResolution 725 * @return 726 * Defines the resolution level for URIs when serializing URIs. 727 */ 728 protected final UriResolution getUriResolution() { 729 return ctx.getUriResolution(); 730 } 731 732 /** 733 * Configuration property: URI relativity. 734 * 735 * @see Serializer#SERIALIZER_uriRelativity 736 * @return 737 * Defines what relative URIs are relative to when serializing any of the following: 738 */ 739 protected final UriRelativity getUriRelativity() { 740 return ctx.getUriRelativity(); 741 } 742}