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.json;
018
019import static org.apache.juneau.collections.JsonMap.*;
020import static org.apache.juneau.common.utils.Utils.*;
021
022import java.lang.annotation.*;
023import java.nio.charset.*;
024import java.util.*;
025import java.util.concurrent.*;
026
027import org.apache.juneau.*;
028import org.apache.juneau.collections.*;
029import org.apache.juneau.internal.*;
030import org.apache.juneau.serializer.*;
031import org.apache.juneau.utils.*;
032
033/**
034 * Serializes POJO models to JSON.
035 *
036 * <h5 class='topic'>Media types</h5>
037 * <p>
038 * Handles <c>Accept</c> types:  <bc>application/json, text/json</bc>
039 * <p>
040 * Produces <c>Content-Type</c> types:  <bc>application/json</bc>
041 *
042 * <h5 class='topic'>Description</h5>
043 * <p>
044 * The conversion is as follows...
045 * <ul class='spaced-list'>
046 *    <li>
047 *       Maps (e.g. {@link HashMap HashMaps}, {@link TreeMap TreeMaps}) are converted to JSON objects.
048 *    <li>
049 *       Collections (e.g. {@link HashSet HashSets}, {@link LinkedList LinkedLists}) and Java arrays are converted to
050 *       JSON arrays.
051 *    <li>
052 *       {@link String Strings} are converted to JSON strings.
053 *    <li>
054 *       {@link Number Numbers} (e.g. {@link Integer}, {@link Long}, {@link Double}) are converted to JSON numbers.
055 *    <li>
056 *       {@link Boolean Booleans} are converted to JSON booleans.
057 *    <li>
058 *       {@code nulls} are converted to JSON nulls.
059 *    <li>
060 *       {@code arrays} are converted to JSON arrays.
061 *    <li>
062 *       {@code beans} are converted to JSON objects.
063 * </ul>
064 *
065 * <p>
066 * The types above are considered "JSON-primitive" object types.
067 * Any non-JSON-primitive object types are transformed into JSON-primitive object types through
068 * {@link org.apache.juneau.swap.ObjectSwap ObjectSwaps} associated through the
069 * {@link org.apache.juneau.BeanContext.Builder#swaps(Class...)} method.
070 * Several default transforms are provided for transforming Dates, Enums, Iterators, etc...
071 *
072 * <p>
073 * This serializer provides several serialization options.
074 * Typically, one of the predefined DEFAULT serializers will be sufficient.
075 * However, custom serializers can be constructed to fine-tune behavior.
076 *
077 * <h5 class='topic'>Behavior-specific subclasses</h5>
078 * <p>
079 * The following direct subclasses are provided for convenience:
080 * <ul class='spaced-list'>
081 *    <li>
082 *       {@link Json5Serializer} - Default serializer, single quotes, simple mode.
083 *    <li>
084 *       {@link Json5Serializer.Readable} - Default serializer, single quotes, simple mode, with whitespace.
085 * </ul>
086 *
087 * <h5 class='section'>Example:</h5>
088 * <p class='bjava'>
089 *    <jc>// Use one of the default serializers to serialize a POJO</jc>
090 *    String <jv>json</jv> = JsonSerializer.<jsf>DEFAULT</jsf>.serialize(<jv>someObject</jv>);
091 *
092 *    <jc>// Create a custom serializer for lax syntax using single quote characters</jc>
093 *    JsonSerializer <jv>serializer</jv> = JsonSerializer.<jsm>create</jsm>().simple().sq().build();
094 *
095 *    <jc>// Clone an existing serializer and modify it to use single-quotes</jc>
096 *    <jv>serializer</jv> = JsonSerializer.<jsf>DEFAULT</jsf>.copy().sq().build();
097 *
098 *    <jc>// Serialize a POJO to JSON</jc>
099 *    String <jv>json</jv> = <jv>serializer</jv>.serialize(<jv>someObject</jv>);
100 * </p>
101 *
102 * <h5 class='section'>Notes:</h5><ul>
103 *    <li class='note'>This class is thread safe and reusable.
104 * </ul>
105 *
106 * <h5 class='section'>See Also:</h5><ul>
107 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JsonBasics">JSON Basics</a>
108
109 * </ul>
110 */
111public class JsonSerializer extends WriterSerializer implements JsonMetaProvider {
112
113   //-------------------------------------------------------------------------------------------------------------------
114   // Static
115   //-------------------------------------------------------------------------------------------------------------------
116
117   /** Default serializer, all default settings.*/
118   public static final JsonSerializer DEFAULT = new JsonSerializer(create());
119
120   /** Default serializer, single quotes, {@link JsonSerializer.Builder#simpleAttrs() simple mode}, sorted bean properties. */
121   public static final JsonSerializer DEFAULT_SORTED = new JsonSerializer(create().sortProperties());
122
123   /** Default serializer, all default settings.*/
124   public static final JsonSerializer DEFAULT_READABLE = new Readable(create());
125
126   /**
127    * Creates a new builder for this object.
128    *
129    * @return A new builder.
130    */
131   public static Builder create() {
132      return new Builder();
133   }
134
135   //-------------------------------------------------------------------------------------------------------------------
136   // Static subclasses
137   //-------------------------------------------------------------------------------------------------------------------
138
139   /** Default serializer, with whitespace. */
140   public static class Readable extends JsonSerializer {
141
142      /**
143       * Constructor.
144       *
145       * @param builder The builder for this object.
146       */
147      public Readable(Builder builder) {
148         super(builder.useWhitespace());
149      }
150   }
151
152   /**
153    * Default serializer, single quotes, simple mode, with whitespace and recursion detection.
154    * Note that recursion detection introduces a small performance penalty.
155    */
156   public static class ReadableSafe extends JsonSerializer {
157
158      /**
159       * Constructor.
160       *
161       * @param builder The builder for this object.
162       */
163      public ReadableSafe(Builder builder) {
164         super(builder.simpleAttrs().useWhitespace().detectRecursions());
165      }
166   }
167
168   //-------------------------------------------------------------------------------------------------------------------
169   // Builder
170   //-------------------------------------------------------------------------------------------------------------------
171
172   /**
173    * Builder class.
174    */
175   public static class Builder extends WriterSerializer.Builder {
176
177      private static final Cache<HashKey,JsonSerializer> CACHE = Cache.of(HashKey.class, JsonSerializer.class).build();
178
179      boolean addBeanTypesJson, escapeSolidus, simpleAttrs;
180
181      /**
182       * Constructor, default settings.
183       */
184      protected Builder() {
185         produces("application/json");
186         accept("application/json,text/json");
187         addBeanTypesJson = env("JsonSerializer.addBeanTypes", false);
188         escapeSolidus = env("JsonSerializer.escapeSolidus", false);
189         simpleAttrs = env("JsonSerializer.simpleAttrs", false);
190      }
191
192      /**
193       * Copy constructor.
194       *
195       * @param copyFrom The bean to copy from.
196       */
197      protected Builder(JsonSerializer copyFrom) {
198         super(copyFrom);
199         addBeanTypesJson = copyFrom.addBeanTypesJson;
200         escapeSolidus = copyFrom.escapeSolidus;
201         simpleAttrs = copyFrom.simpleAttrs;
202      }
203
204      /**
205       * Copy constructor.
206       *
207       * @param copyFrom The builder to copy from.
208       */
209      protected Builder(Builder copyFrom) {
210         super(copyFrom);
211         addBeanTypesJson = copyFrom.addBeanTypesJson;
212         escapeSolidus = copyFrom.escapeSolidus;
213         simpleAttrs = copyFrom.simpleAttrs;
214      }
215
216      @Override /* Context.Builder */
217      public Builder copy() {
218         return new Builder(this);
219      }
220
221      @Override /* Context.Builder */
222      public JsonSerializer build() {
223         return cache(CACHE).build(JsonSerializer.class);
224      }
225
226      @Override /* Context.Builder */
227      public HashKey hashKey() {
228         return HashKey.of(
229            super.hashKey(),
230            addBeanTypesJson,
231            escapeSolidus,
232            simpleAttrs
233         );
234      }
235
236      //-----------------------------------------------------------------------------------------------------------------
237      // Properties
238      //-----------------------------------------------------------------------------------------------------------------
239
240      /**
241       * Add <js>"_type"</js> properties when needed.
242       *
243       * <p>
244       * If <jk>true</jk>, then <js>"_type"</js> properties will be added to beans if their type cannot be inferred
245       * through reflection.
246       *
247       * <p>
248       * When present, this value overrides the {@link org.apache.juneau.serializer.Serializer.Builder#addBeanTypes()} setting and is
249       * provided to customize the behavior of specific serializers in a {@link SerializerSet}.
250       *
251       * @return This object.
252       */
253      public Builder addBeanTypesJson() {
254         return addBeanTypesJson(true);
255      }
256
257      /**
258       * Same as {@link #addBeanTypesJson()} but allows you to explicitly specify the value.
259       *
260       * @param value The value for this setting.
261       * @return This object.
262       */
263      public Builder addBeanTypesJson(boolean value) {
264         addBeanTypesJson = value;
265         return this;
266      }
267
268      /**
269       * Prefix solidus <js>'/'</js> characters with escapes.
270       *
271       * <p>
272       * If enabled, solidus (e.g. slash) characters should be escaped.
273       *
274       * <p>
275       * The JSON specification allows for either format.
276       * <br>However, if you're embedding JSON in an HTML script tag, this setting prevents confusion when trying to serialize
277       * <xt>&lt;\/script&gt;</xt>.
278       *
279       * <h5 class='section'>Example:</h5>
280       * <p class='bjava'>
281       *    <jc>// Create a JSON serializer that escapes solidus characters.</jc>
282       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
283       *       .<jsm>create</jsm>()
284       *       .simple()
285       *       .escapeSolidus()
286       *       .build();
287       *
288       *    <jc>// Produces: "{foo:'&lt;\/bar&gt;'"</jc>
289       *    String <jv>json</jv> = <jv>serializer</jv>.serialize(JsonMap.<jsm>of</jsm>(<js>"foo"</js>, <js>"&lt;/bar&gt;"</js>);
290       * </p>
291       *
292       * @return This object.
293       */
294      public Builder escapeSolidus() {
295         return escapeSolidus(true);
296      }
297
298      /**
299       * Same as {@link #escapeSolidus()} but allows you to explicitly specify the value.
300       *
301       * @param value The value for this setting.
302       * @return This object.
303       */
304      public Builder escapeSolidus(boolean value) {
305         escapeSolidus = value;
306         return this;
307      }
308
309      /**
310       * Simple JSON attributes mode.
311       *
312       * <p>
313       * If enabled, JSON attribute names will only be quoted when necessary.
314       * <br>Otherwise, they are always quoted.
315       *
316       * <p>
317       * Attributes do not need to be quoted when they conform to the following:
318       * <ol class='spaced-list'>
319       *    <li>They start with an ASCII character or <js>'_'</js>.
320       *    <li>They contain only ASCII characters or numbers or <js>'_'</js>.
321       *    <li>They are not one of the following reserved words:
322       *       <p class='bcode'>
323       *    arguments, break, case, catch, class, const, continue, debugger, default,
324       *    delete, do, else, enum, eval, export, extends, false, finally, for, function,
325       *    if, implements, import, in, instanceof, interface, let, new, null, package,
326       *    private, protected, public, return, static, super, switch, this, throw,
327       *    true, try, typeof, var, void, while, with, undefined, yield
328       *       </p>
329       * </ol>
330       *
331       * <h5 class='section'>Example:</h5>
332       * <p class='bjava'>
333       *    <jc>// Create a JSON serializer in normal mode.</jc>
334       *    WriterSerializer <jv>serializer1</jv> = JsonSerializer
335       *       .<jsm>create</jsm>()
336       *       .build();
337       *
338       *    <jc>// Create a JSON serializer in simple mode.</jc>
339       *    WriterSerializer <jv>serializer2</jv> = JsonSerializer
340       *       .<jsm>create</jsm>()
341       *       .simpleAttrs()
342       *       .build();
343       *
344       *    JsonMap <jv>myMap</jv> = JsonMap.<jsm>of</jsm>(
345       *       <js>"foo"</js>, <js>"x1"</js>,
346       *       <js>"_bar"</js>, <js>"x2"</js>,
347       *       <js>" baz "</js>, <js>"x3"</js>,
348       *       <js>"123"</js>, <js>"x4"</js>,
349       *       <js>"return"</js>, <js>"x5"</js>,
350       *       <js>""</js>, <js>"x6"</js>
351       *  );
352       *
353       *    <jc>// Produces:</jc>
354       *    <jc>// {</jc>
355       *    <jc>//   "foo": "x1"</jc>
356       *    <jc>//   "_bar": "x2"</jc>
357       *    <jc>//   " baz ": "x3"</jc>
358       *    <jc>//   "123": "x4"</jc>
359       *    <jc>//   "return": "x5"</jc>
360       *    <jc>//   "": "x6"</jc>
361       *    <jc>// }</jc>
362       *    String <jv>json1</jv> = <jv>serializer1</jv>.serialize(<jv>myMap</jv>);
363       *
364       *    <jc>// Produces:</jc>
365       *    <jc>// {</jc>
366       *    <jc>//   foo: "x1"</jc>
367       *    <jc>//   _bar: "x2"</jc>
368       *    <jc>//   " baz ": "x3"</jc>
369       *    <jc>//   "123": "x4"</jc>
370       *    <jc>//   "return": "x5"</jc>
371       *    <jc>//   "": "x6"</jc>
372       *    <jc>// }</jc>
373       *    String <jv>json2</jv> = <jv>serializer2</jv>.serialize(<jv>myMap</jv>);
374       * </p>
375       *
376       * @return This object.
377       */
378      public Builder simpleAttrs() {
379         return simpleAttrs(true);
380      }
381
382      /**
383       * Same as {@link #simpleAttrs()} but allows you to explicitly specify the value.
384       *
385       * @param value The value for this setting.
386       * @return This object.
387       */
388      public Builder simpleAttrs(boolean value) {
389         simpleAttrs = value;
390         return this;
391      }
392
393      /**
394       * Simple JSON mode and single quote.
395       *
396       * <p>
397       * Shortcut for calling <c>simple().sq()</c>.
398       *
399       * <h5 class='section'>See Also:</h5><ul>
400       *    <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializer.Builder#quoteChar(char)}
401       * </ul>
402       *
403       * @return This object.
404       */
405      public Builder json5() {
406         return simpleAttrs().sq();
407      }
408      @Override /* Overridden from Builder */
409      public Builder annotations(Annotation...values) {
410         super.annotations(values);
411         return this;
412      }
413
414      @Override /* Overridden from Builder */
415      public Builder apply(AnnotationWorkList work) {
416         super.apply(work);
417         return this;
418      }
419
420      @Override /* Overridden from Builder */
421      public Builder applyAnnotations(Object...from) {
422         super.applyAnnotations(from);
423         return this;
424      }
425
426      @Override /* Overridden from Builder */
427      public Builder applyAnnotations(Class<?>...from) {
428         super.applyAnnotations(from);
429         return this;
430      }
431
432      @Override /* Overridden from Builder */
433      public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) {
434         super.cache(value);
435         return this;
436      }
437
438      @Override /* Overridden from Builder */
439      public Builder debug() {
440         super.debug();
441         return this;
442      }
443
444      @Override /* Overridden from Builder */
445      public Builder debug(boolean value) {
446         super.debug(value);
447         return this;
448      }
449
450      @Override /* Overridden from Builder */
451      public Builder impl(Context value) {
452         super.impl(value);
453         return this;
454      }
455
456      @Override /* Overridden from Builder */
457      public Builder type(Class<? extends org.apache.juneau.Context> value) {
458         super.type(value);
459         return this;
460      }
461
462      @Override /* Overridden from Builder */
463      public Builder beanClassVisibility(Visibility value) {
464         super.beanClassVisibility(value);
465         return this;
466      }
467
468      @Override /* Overridden from Builder */
469      public Builder beanConstructorVisibility(Visibility value) {
470         super.beanConstructorVisibility(value);
471         return this;
472      }
473
474      @Override /* Overridden from Builder */
475      public Builder beanContext(BeanContext value) {
476         super.beanContext(value);
477         return this;
478      }
479
480      @Override /* Overridden from Builder */
481      public Builder beanContext(BeanContext.Builder value) {
482         super.beanContext(value);
483         return this;
484      }
485
486      @Override /* Overridden from Builder */
487      public Builder beanDictionary(java.lang.Class<?>...values) {
488         super.beanDictionary(values);
489         return this;
490      }
491
492      @Override /* Overridden from Builder */
493      public Builder beanFieldVisibility(Visibility value) {
494         super.beanFieldVisibility(value);
495         return this;
496      }
497
498      @Override /* Overridden from Builder */
499      public Builder beanInterceptor(Class<?> on, Class<? extends org.apache.juneau.swap.BeanInterceptor<?>> value) {
500         super.beanInterceptor(on, value);
501         return this;
502      }
503
504      @Override /* Overridden from Builder */
505      public Builder beanMapPutReturnsOldValue() {
506         super.beanMapPutReturnsOldValue();
507         return this;
508      }
509
510      @Override /* Overridden from Builder */
511      public Builder beanMethodVisibility(Visibility value) {
512         super.beanMethodVisibility(value);
513         return this;
514      }
515
516      @Override /* Overridden from Builder */
517      public Builder beanProperties(Map<String,Object> values) {
518         super.beanProperties(values);
519         return this;
520      }
521
522      @Override /* Overridden from Builder */
523      public Builder beanProperties(Class<?> beanClass, String properties) {
524         super.beanProperties(beanClass, properties);
525         return this;
526      }
527
528      @Override /* Overridden from Builder */
529      public Builder beanProperties(String beanClassName, String properties) {
530         super.beanProperties(beanClassName, properties);
531         return this;
532      }
533
534      @Override /* Overridden from Builder */
535      public Builder beanPropertiesExcludes(Map<String,Object> values) {
536         super.beanPropertiesExcludes(values);
537         return this;
538      }
539
540      @Override /* Overridden from Builder */
541      public Builder beanPropertiesExcludes(Class<?> beanClass, String properties) {
542         super.beanPropertiesExcludes(beanClass, properties);
543         return this;
544      }
545
546      @Override /* Overridden from Builder */
547      public Builder beanPropertiesExcludes(String beanClassName, String properties) {
548         super.beanPropertiesExcludes(beanClassName, properties);
549         return this;
550      }
551
552      @Override /* Overridden from Builder */
553      public Builder beanPropertiesReadOnly(Map<String,Object> values) {
554         super.beanPropertiesReadOnly(values);
555         return this;
556      }
557
558      @Override /* Overridden from Builder */
559      public Builder beanPropertiesReadOnly(Class<?> beanClass, String properties) {
560         super.beanPropertiesReadOnly(beanClass, properties);
561         return this;
562      }
563
564      @Override /* Overridden from Builder */
565      public Builder beanPropertiesReadOnly(String beanClassName, String properties) {
566         super.beanPropertiesReadOnly(beanClassName, properties);
567         return this;
568      }
569
570      @Override /* Overridden from Builder */
571      public Builder beanPropertiesWriteOnly(Map<String,Object> values) {
572         super.beanPropertiesWriteOnly(values);
573         return this;
574      }
575
576      @Override /* Overridden from Builder */
577      public Builder beanPropertiesWriteOnly(Class<?> beanClass, String properties) {
578         super.beanPropertiesWriteOnly(beanClass, properties);
579         return this;
580      }
581
582      @Override /* Overridden from Builder */
583      public Builder beanPropertiesWriteOnly(String beanClassName, String properties) {
584         super.beanPropertiesWriteOnly(beanClassName, properties);
585         return this;
586      }
587
588      @Override /* Overridden from Builder */
589      public Builder beansRequireDefaultConstructor() {
590         super.beansRequireDefaultConstructor();
591         return this;
592      }
593
594      @Override /* Overridden from Builder */
595      public Builder beansRequireSerializable() {
596         super.beansRequireSerializable();
597         return this;
598      }
599
600      @Override /* Overridden from Builder */
601      public Builder beansRequireSettersForGetters() {
602         super.beansRequireSettersForGetters();
603         return this;
604      }
605
606      @Override /* Overridden from Builder */
607      public Builder dictionaryOn(Class<?> on, java.lang.Class<?>...values) {
608         super.dictionaryOn(on, values);
609         return this;
610      }
611
612      @Override /* Overridden from Builder */
613      public Builder disableBeansRequireSomeProperties() {
614         super.disableBeansRequireSomeProperties();
615         return this;
616      }
617
618      @Override /* Overridden from Builder */
619      public Builder disableIgnoreMissingSetters() {
620         super.disableIgnoreMissingSetters();
621         return this;
622      }
623
624      @Override /* Overridden from Builder */
625      public Builder disableIgnoreTransientFields() {
626         super.disableIgnoreTransientFields();
627         return this;
628      }
629
630      @Override /* Overridden from Builder */
631      public Builder disableIgnoreUnknownNullBeanProperties() {
632         super.disableIgnoreUnknownNullBeanProperties();
633         return this;
634      }
635
636      @Override /* Overridden from Builder */
637      public Builder disableInterfaceProxies() {
638         super.disableInterfaceProxies();
639         return this;
640      }
641
642      @Override /* Overridden from Builder */
643      public <T> Builder example(Class<T> pojoClass, T o) {
644         super.example(pojoClass, o);
645         return this;
646      }
647
648      @Override /* Overridden from Builder */
649      public <T> Builder example(Class<T> pojoClass, String json) {
650         super.example(pojoClass, json);
651         return this;
652      }
653
654      @Override /* Overridden from Builder */
655      public Builder findFluentSetters() {
656         super.findFluentSetters();
657         return this;
658      }
659
660      @Override /* Overridden from Builder */
661      public Builder findFluentSetters(Class<?> on) {
662         super.findFluentSetters(on);
663         return this;
664      }
665
666      @Override /* Overridden from Builder */
667      public Builder ignoreInvocationExceptionsOnGetters() {
668         super.ignoreInvocationExceptionsOnGetters();
669         return this;
670      }
671
672      @Override /* Overridden from Builder */
673      public Builder ignoreInvocationExceptionsOnSetters() {
674         super.ignoreInvocationExceptionsOnSetters();
675         return this;
676      }
677
678      @Override /* Overridden from Builder */
679      public Builder ignoreUnknownBeanProperties() {
680         super.ignoreUnknownBeanProperties();
681         return this;
682      }
683
684      @Override /* Overridden from Builder */
685      public Builder ignoreUnknownEnumValues() {
686         super.ignoreUnknownEnumValues();
687         return this;
688      }
689
690      @Override /* Overridden from Builder */
691      public Builder implClass(Class<?> interfaceClass, Class<?> implClass) {
692         super.implClass(interfaceClass, implClass);
693         return this;
694      }
695
696      @Override /* Overridden from Builder */
697      public Builder implClasses(Map<Class<?>,Class<?>> values) {
698         super.implClasses(values);
699         return this;
700      }
701
702      @Override /* Overridden from Builder */
703      public Builder interfaceClass(Class<?> on, Class<?> value) {
704         super.interfaceClass(on, value);
705         return this;
706      }
707
708      @Override /* Overridden from Builder */
709      public Builder interfaces(java.lang.Class<?>...value) {
710         super.interfaces(value);
711         return this;
712      }
713
714      @Override /* Overridden from Builder */
715      public Builder locale(Locale value) {
716         super.locale(value);
717         return this;
718      }
719
720      @Override /* Overridden from Builder */
721      public Builder mediaType(MediaType value) {
722         super.mediaType(value);
723         return this;
724      }
725
726      @Override /* Overridden from Builder */
727      public Builder notBeanClasses(java.lang.Class<?>...values) {
728         super.notBeanClasses(values);
729         return this;
730      }
731
732      @Override /* Overridden from Builder */
733      public Builder notBeanPackages(String...values) {
734         super.notBeanPackages(values);
735         return this;
736      }
737
738      @Override /* Overridden from Builder */
739      public Builder propertyNamer(Class<? extends org.apache.juneau.PropertyNamer> value) {
740         super.propertyNamer(value);
741         return this;
742      }
743
744      @Override /* Overridden from Builder */
745      public Builder propertyNamer(Class<?> on, Class<? extends org.apache.juneau.PropertyNamer> value) {
746         super.propertyNamer(on, value);
747         return this;
748      }
749
750      @Override /* Overridden from Builder */
751      public Builder sortProperties() {
752         super.sortProperties();
753         return this;
754      }
755
756      @Override /* Overridden from Builder */
757      public Builder sortProperties(java.lang.Class<?>...on) {
758         super.sortProperties(on);
759         return this;
760      }
761
762      @Override /* Overridden from Builder */
763      public Builder stopClass(Class<?> on, Class<?> value) {
764         super.stopClass(on, value);
765         return this;
766      }
767
768      @Override /* Overridden from Builder */
769      public <T, S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction) {
770         super.swap(normalClass, swappedClass, swapFunction);
771         return this;
772      }
773
774      @Override /* Overridden from Builder */
775      public <T, S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction, ThrowingFunction<S,T> unswapFunction) {
776         super.swap(normalClass, swappedClass, swapFunction, unswapFunction);
777         return this;
778      }
779
780      @Override /* Overridden from Builder */
781      public Builder swaps(Object...values) {
782         super.swaps(values);
783         return this;
784      }
785
786      @Override /* Overridden from Builder */
787      public Builder swaps(Class<?>...values) {
788         super.swaps(values);
789         return this;
790      }
791
792      @Override /* Overridden from Builder */
793      public Builder timeZone(TimeZone value) {
794         super.timeZone(value);
795         return this;
796      }
797
798      @Override /* Overridden from Builder */
799      public Builder typeName(Class<?> on, String value) {
800         super.typeName(on, value);
801         return this;
802      }
803
804      @Override /* Overridden from Builder */
805      public Builder typePropertyName(String value) {
806         super.typePropertyName(value);
807         return this;
808      }
809
810      @Override /* Overridden from Builder */
811      public Builder typePropertyName(Class<?> on, String value) {
812         super.typePropertyName(on, value);
813         return this;
814      }
815
816      @Override /* Overridden from Builder */
817      public Builder useEnumNames() {
818         super.useEnumNames();
819         return this;
820      }
821
822      @Override /* Overridden from Builder */
823      public Builder useJavaBeanIntrospector() {
824         super.useJavaBeanIntrospector();
825         return this;
826      }
827
828      @Override /* Overridden from Builder */
829      public Builder detectRecursions() {
830         super.detectRecursions();
831         return this;
832      }
833
834      @Override /* Overridden from Builder */
835      public Builder detectRecursions(boolean value) {
836         super.detectRecursions(value);
837         return this;
838      }
839
840      @Override /* Overridden from Builder */
841      public Builder ignoreRecursions() {
842         super.ignoreRecursions();
843         return this;
844      }
845
846      @Override /* Overridden from Builder */
847      public Builder ignoreRecursions(boolean value) {
848         super.ignoreRecursions(value);
849         return this;
850      }
851
852      @Override /* Overridden from Builder */
853      public Builder initialDepth(int value) {
854         super.initialDepth(value);
855         return this;
856      }
857
858      @Override /* Overridden from Builder */
859      public Builder maxDepth(int value) {
860         super.maxDepth(value);
861         return this;
862      }
863
864      @Override /* Overridden from Builder */
865      public Builder accept(String value) {
866         super.accept(value);
867         return this;
868      }
869
870      @Override /* Overridden from Builder */
871      public Builder addBeanTypes() {
872         super.addBeanTypes();
873         return this;
874      }
875
876      @Override /* Overridden from Builder */
877      public Builder addBeanTypes(boolean value) {
878         super.addBeanTypes(value);
879         return this;
880      }
881
882      @Override /* Overridden from Builder */
883      public Builder addRootType() {
884         super.addRootType();
885         return this;
886      }
887
888      @Override /* Overridden from Builder */
889      public Builder addRootType(boolean value) {
890         super.addRootType(value);
891         return this;
892      }
893
894      @Override /* Overridden from Builder */
895      public Builder keepNullProperties() {
896         super.keepNullProperties();
897         return this;
898      }
899
900      @Override /* Overridden from Builder */
901      public Builder keepNullProperties(boolean value) {
902         super.keepNullProperties(value);
903         return this;
904      }
905
906      @Override /* Overridden from Builder */
907      public Builder listener(Class<? extends org.apache.juneau.serializer.SerializerListener> value) {
908         super.listener(value);
909         return this;
910      }
911
912      @Override /* Overridden from Builder */
913      public Builder produces(String value) {
914         super.produces(value);
915         return this;
916      }
917
918      @Override /* Overridden from Builder */
919      public Builder sortCollections() {
920         super.sortCollections();
921         return this;
922      }
923
924      @Override /* Overridden from Builder */
925      public Builder sortCollections(boolean value) {
926         super.sortCollections(value);
927         return this;
928      }
929
930      @Override /* Overridden from Builder */
931      public Builder sortMaps() {
932         super.sortMaps();
933         return this;
934      }
935
936      @Override /* Overridden from Builder */
937      public Builder sortMaps(boolean value) {
938         super.sortMaps(value);
939         return this;
940      }
941
942      @Override /* Overridden from Builder */
943      public Builder trimEmptyCollections() {
944         super.trimEmptyCollections();
945         return this;
946      }
947
948      @Override /* Overridden from Builder */
949      public Builder trimEmptyCollections(boolean value) {
950         super.trimEmptyCollections(value);
951         return this;
952      }
953
954      @Override /* Overridden from Builder */
955      public Builder trimEmptyMaps() {
956         super.trimEmptyMaps();
957         return this;
958      }
959
960      @Override /* Overridden from Builder */
961      public Builder trimEmptyMaps(boolean value) {
962         super.trimEmptyMaps(value);
963         return this;
964      }
965
966      @Override /* Overridden from Builder */
967      public Builder trimStrings() {
968         super.trimStrings();
969         return this;
970      }
971
972      @Override /* Overridden from Builder */
973      public Builder trimStrings(boolean value) {
974         super.trimStrings(value);
975         return this;
976      }
977
978      @Override /* Overridden from Builder */
979      public Builder uriContext(UriContext value) {
980         super.uriContext(value);
981         return this;
982      }
983
984      @Override /* Overridden from Builder */
985      public Builder uriRelativity(UriRelativity value) {
986         super.uriRelativity(value);
987         return this;
988      }
989
990      @Override /* Overridden from Builder */
991      public Builder uriResolution(UriResolution value) {
992         super.uriResolution(value);
993         return this;
994      }
995
996      @Override /* Overridden from Builder */
997      public Builder fileCharset(Charset value) {
998         super.fileCharset(value);
999         return this;
1000      }
1001
1002      @Override /* Overridden from Builder */
1003      public Builder maxIndent(int value) {
1004         super.maxIndent(value);
1005         return this;
1006      }
1007
1008      @Override /* Overridden from Builder */
1009      public Builder quoteChar(char value) {
1010         super.quoteChar(value);
1011         return this;
1012      }
1013
1014      @Override /* Overridden from Builder */
1015      public Builder quoteCharOverride(char value) {
1016         super.quoteCharOverride(value);
1017         return this;
1018      }
1019
1020      @Override /* Overridden from Builder */
1021      public Builder sq() {
1022         super.sq();
1023         return this;
1024      }
1025
1026      @Override /* Overridden from Builder */
1027      public Builder streamCharset(Charset value) {
1028         super.streamCharset(value);
1029         return this;
1030      }
1031
1032      @Override /* Overridden from Builder */
1033      public Builder useWhitespace() {
1034         super.useWhitespace();
1035         return this;
1036      }
1037
1038      @Override /* Overridden from Builder */
1039      public Builder useWhitespace(boolean value) {
1040         super.useWhitespace(value);
1041         return this;
1042      }
1043
1044      @Override /* Overridden from Builder */
1045      public Builder ws() {
1046         super.ws();
1047         return this;
1048      }
1049   }
1050
1051   //-------------------------------------------------------------------------------------------------------------------
1052   // Instance
1053   //-------------------------------------------------------------------------------------------------------------------
1054
1055   final boolean addBeanTypesJson, escapeSolidus, simpleAttrs;
1056
1057   private final boolean addBeanTypes;
1058   private final Map<ClassMeta<?>,JsonClassMeta> jsonClassMetas = new ConcurrentHashMap<>();
1059   private final Map<BeanPropertyMeta,JsonBeanPropertyMeta> jsonBeanPropertyMetas = new ConcurrentHashMap<>();
1060
1061   private volatile JsonSchemaSerializer schemaSerializer;
1062
1063   /**
1064    * Constructor.
1065    *
1066    * @param builder The builder for this object.
1067    */
1068   public JsonSerializer(Builder builder) {
1069      super(builder);
1070      addBeanTypesJson = builder.addBeanTypesJson;
1071      simpleAttrs = builder.simpleAttrs;
1072      escapeSolidus = builder.escapeSolidus;
1073
1074      addBeanTypes = addBeanTypesJson || super.isAddBeanTypes();
1075   }
1076
1077   @Override /* Context */
1078   public Builder copy() {
1079      return new Builder(this);
1080   }
1081
1082   @Override /* Context */
1083   public JsonSerializerSession.Builder createSession() {
1084      return JsonSerializerSession.create(this);
1085   }
1086
1087   @Override /* Context */
1088   public JsonSerializerSession getSession() {
1089      return createSession().build();
1090   }
1091
1092   /**
1093    * Returns the schema serializer based on the settings of this serializer.
1094    *
1095    * <p>
1096    * Note that this method creates a builder initialized to all default settings, whereas {@link #copy()} copies
1097    * the settings of the object called on.
1098    *
1099    * @return The schema serializer.
1100    */
1101   public JsonSchemaSerializer getSchemaSerializer() {
1102      if (schemaSerializer == null)
1103         schemaSerializer = JsonSchemaSerializer.create().beanContext(getBeanContext()).build();
1104      return schemaSerializer;
1105   }
1106
1107   //-----------------------------------------------------------------------------------------------------------------
1108   // Extended metadata
1109   //-----------------------------------------------------------------------------------------------------------------
1110
1111   @Override /* JsonMetaProvider */
1112   public JsonClassMeta getJsonClassMeta(ClassMeta<?> cm) {
1113      JsonClassMeta m = jsonClassMetas.get(cm);
1114      if (m == null) {
1115         m = new JsonClassMeta(cm, this);
1116         jsonClassMetas.put(cm, m);
1117      }
1118      return m;
1119   }
1120
1121   @Override /* JsonMetaProvider */
1122   public JsonBeanPropertyMeta getJsonBeanPropertyMeta(BeanPropertyMeta bpm) {
1123      if (bpm == null)
1124         return JsonBeanPropertyMeta.DEFAULT;
1125      JsonBeanPropertyMeta m = jsonBeanPropertyMetas.get(bpm);
1126      if (m == null) {
1127         m = new JsonBeanPropertyMeta(bpm.getDelegateFor(), this);
1128         jsonBeanPropertyMetas.put(bpm, m);
1129      }
1130      return m;
1131   }
1132
1133   //-----------------------------------------------------------------------------------------------------------------
1134   // Properties
1135   //-----------------------------------------------------------------------------------------------------------------
1136
1137   /**
1138    * Add <js>"_type"</js> properties when needed.
1139    *
1140    * @see Builder#addBeanTypesJson()
1141    * @return
1142    *    <jk>true</jk> if <js>"_type"</js> properties will be added to beans if their type cannot be inferred
1143    *    through reflection.
1144    */
1145   @Override
1146   protected final boolean isAddBeanTypes() {
1147      return addBeanTypes;
1148   }
1149
1150   /**
1151    * Prefix solidus <js>'/'</js> characters with escapes.
1152    *
1153    * @see Builder#escapeSolidus()
1154    * @return
1155    *    <jk>true</jk> if solidus (e.g. slash) characters should be escaped.
1156    */
1157   protected final boolean isEscapeSolidus() {
1158      return escapeSolidus;
1159   }
1160
1161   /**
1162    * Simple JSON mode.
1163    *
1164    * @see Builder#simpleAttrs()
1165    * @return
1166    *    <jk>true</jk> if JSON attribute names will only be quoted when necessary.
1167    *    <br>Otherwise, they are always quoted.
1168    */
1169   protected final boolean isSimpleAttrs() {
1170      return simpleAttrs;
1171   }
1172
1173   //-----------------------------------------------------------------------------------------------------------------
1174   // Other methods
1175   //-----------------------------------------------------------------------------------------------------------------
1176
1177   @Override /* Context */
1178   protected JsonMap properties() {
1179      return filteredMap("simpleAttrs", simpleAttrs, "escapeSolidus", escapeSolidus, "addBeanTypesJson", addBeanTypesJson);
1180   }
1181}