View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.serializer;
18  
19  import static org.apache.juneau.commons.utils.AssertionUtils.*;
20  import static org.apache.juneau.commons.utils.IoUtils.*;
21  import static org.apache.juneau.commons.utils.ThrowableUtils.*;
22  import static org.apache.juneau.commons.utils.Utils.*;
23  
24  import java.lang.annotation.*;
25  import java.nio.charset.*;
26  import java.util.*;
27  
28  import org.apache.juneau.*;
29  import org.apache.juneau.commons.collections.*;
30  import org.apache.juneau.commons.function.*;
31  import org.apache.juneau.commons.reflect.*;
32  import org.apache.juneau.json.*;
33  
34  /**
35   * Subclass of {@link Serializer} for character-based serializers.
36   *
37   * <h5 class='section'>Notes:</h5><ul>
38   * 	<li class='note'>This class is thread safe and reusable.
39   * </ul>
40   *
41   * <h5 class='section'>See Also:</h5><ul>
42   * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a>
43   * </ul>
44   */
45  public class WriterSerializer extends Serializer {
46  	/**
47  	 * Builder class.
48  	 */
49  	public static class Builder extends Serializer.Builder {
50  
51  		private boolean useWhitespace;
52  		private Character quoteChar;
53  		private Character quoteCharOverride;
54  		private Charset fileCharset;
55  		private Charset streamCharset;
56  		private int maxIndent;
57  
58  		/**
59  		 * Constructor, default settings.
60  		 */
61  		protected Builder() {
62  			fileCharset = Charset.defaultCharset();
63  			streamCharset = UTF8;
64  			maxIndent = env("WriterSerializer.maxIndent", 100);
65  			quoteChar = env("WriterSerializer.quoteChar").map(x -> (x.length() > 0 ? x.charAt(0) : null)).orElse(null);
66  			quoteCharOverride = env("WriterSerializer.quoteCharOverride").map(x -> (x.length() > 0 ? x.charAt(0) : null)).orElse(null);
67  			useWhitespace = env("WriterSerializer.useWhitespace", false);
68  		}
69  
70  		/**
71  		 * Copy constructor.
72  		 *
73  		 * @param copyFrom The builder to copy from.
74  		 * 	<br>Cannot be <jk>null</jk>.
75  		 */
76  		protected Builder(Builder copyFrom) {
77  			super(assertArgNotNull("copyFrom", copyFrom));
78  			fileCharset = copyFrom.fileCharset;
79  			streamCharset = copyFrom.streamCharset;
80  			maxIndent = copyFrom.maxIndent;
81  			quoteChar = copyFrom.quoteChar;
82  			quoteCharOverride = copyFrom.quoteCharOverride;
83  			useWhitespace = copyFrom.useWhitespace;
84  		}
85  
86  		/**
87  		 * Copy constructor.
88  		 *
89  		 * @param copyFrom The bean to copy from.
90  		 * 	<br>Cannot be <jk>null</jk>.
91  		 */
92  		protected Builder(WriterSerializer copyFrom) {
93  			super(assertArgNotNull("copyFrom", copyFrom));
94  			fileCharset = copyFrom.getFileCharset();
95  			streamCharset = copyFrom.getStreamCharset();
96  			maxIndent = copyFrom.maxIndent;
97  			quoteChar = copyFrom.quoteChar;
98  			quoteCharOverride = copyFrom.quoteCharOverride;
99  			useWhitespace = copyFrom.useWhitespace;
100 		}
101 
102 		@Override /* Overridden from Builder */
103 		public Builder accept(String value) {
104 			super.accept(value);
105 			return this;
106 		}
107 
108 		@Override /* Overridden from Builder */
109 		public Builder addBeanTypes() {
110 			super.addBeanTypes();
111 			return this;
112 		}
113 
114 		@Override /* Overridden from Builder */
115 		public Builder addBeanTypes(boolean value) {
116 			super.addBeanTypes(value);
117 			return this;
118 		}
119 
120 		@Override /* Overridden from Builder */
121 		public Builder addRootType() {
122 			super.addRootType();
123 			return this;
124 		}
125 
126 		@Override /* Overridden from Builder */
127 		public Builder addRootType(boolean value) {
128 			super.addRootType(value);
129 			return this;
130 		}
131 
132 		@Override /* Overridden from Builder */
133 		public Builder annotations(Annotation...values) {
134 			super.annotations(values);
135 			return this;
136 		}
137 
138 		@Override /* Overridden from Builder */
139 		public Builder apply(AnnotationWorkList work) {
140 			super.apply(work);
141 			return this;
142 		}
143 
144 		@Override /* Overridden from Builder */
145 		public Builder applyAnnotations(Class<?>...from) {
146 			super.applyAnnotations(from);
147 			return this;
148 		}
149 
150 		@Override /* Overridden from Builder */
151 		public Builder applyAnnotations(Object...from) {
152 			super.applyAnnotations(from);
153 			return this;
154 		}
155 
156 		@Override /* Overridden from Builder */
157 		public Builder beanClassVisibility(Visibility value) {
158 			super.beanClassVisibility(value);
159 			return this;
160 		}
161 
162 		@Override /* Overridden from Builder */
163 		public Builder beanConstructorVisibility(Visibility value) {
164 			super.beanConstructorVisibility(value);
165 			return this;
166 		}
167 
168 		@Override /* Overridden from Builder */
169 		public Builder beanContext(BeanContext value) {
170 			super.beanContext(value);
171 			return this;
172 		}
173 
174 		@Override /* Overridden from Builder */
175 		public Builder beanContext(BeanContext.Builder value) {
176 			super.beanContext(value);
177 			return this;
178 		}
179 
180 		@Override /* Overridden from Builder */
181 		public Builder beanDictionary(java.lang.Class<?>...values) {
182 			super.beanDictionary(values);
183 			return this;
184 		}
185 
186 		@Override /* Overridden from Builder */
187 		public Builder beanFieldVisibility(Visibility value) {
188 			super.beanFieldVisibility(value);
189 			return this;
190 		}
191 
192 		@Override /* Overridden from Builder */
193 		public Builder beanInterceptor(Class<?> on, Class<? extends org.apache.juneau.swap.BeanInterceptor<?>> value) {
194 			super.beanInterceptor(on, value);
195 			return this;
196 		}
197 
198 		@Override /* Overridden from Builder */
199 		public Builder beanMapPutReturnsOldValue() {
200 			super.beanMapPutReturnsOldValue();
201 			return this;
202 		}
203 
204 		@Override /* Overridden from Builder */
205 		public Builder beanMethodVisibility(Visibility value) {
206 			super.beanMethodVisibility(value);
207 			return this;
208 		}
209 
210 		@Override /* Overridden from Builder */
211 		public Builder beanProperties(Class<?> beanClass, String properties) {
212 			super.beanProperties(beanClass, properties);
213 			return this;
214 		}
215 
216 		@Override /* Overridden from Builder */
217 		public Builder beanProperties(Map<String,Object> values) {
218 			super.beanProperties(values);
219 			return this;
220 		}
221 
222 		@Override /* Overridden from Builder */
223 		public Builder beanProperties(String beanClassName, String properties) {
224 			super.beanProperties(beanClassName, properties);
225 			return this;
226 		}
227 
228 		@Override /* Overridden from Builder */
229 		public Builder beanPropertiesExcludes(Class<?> beanClass, String properties) {
230 			super.beanPropertiesExcludes(beanClass, properties);
231 			return this;
232 		}
233 
234 		@Override /* Overridden from Builder */
235 		public Builder beanPropertiesExcludes(Map<String,Object> values) {
236 			super.beanPropertiesExcludes(values);
237 			return this;
238 		}
239 
240 		@Override /* Overridden from Builder */
241 		public Builder beanPropertiesExcludes(String beanClassName, String properties) {
242 			super.beanPropertiesExcludes(beanClassName, properties);
243 			return this;
244 		}
245 
246 		@Override /* Overridden from Builder */
247 		public Builder beanPropertiesReadOnly(Class<?> beanClass, String properties) {
248 			super.beanPropertiesReadOnly(beanClass, properties);
249 			return this;
250 		}
251 
252 		@Override /* Overridden from Builder */
253 		public Builder beanPropertiesReadOnly(Map<String,Object> values) {
254 			super.beanPropertiesReadOnly(values);
255 			return this;
256 		}
257 
258 		@Override /* Overridden from Builder */
259 		public Builder beanPropertiesReadOnly(String beanClassName, String properties) {
260 			super.beanPropertiesReadOnly(beanClassName, properties);
261 			return this;
262 		}
263 
264 		@Override /* Overridden from Builder */
265 		public Builder beanPropertiesWriteOnly(Class<?> beanClass, String properties) {
266 			super.beanPropertiesWriteOnly(beanClass, properties);
267 			return this;
268 		}
269 
270 		@Override /* Overridden from Builder */
271 		public Builder beanPropertiesWriteOnly(Map<String,Object> values) {
272 			super.beanPropertiesWriteOnly(values);
273 			return this;
274 		}
275 
276 		@Override /* Overridden from Builder */
277 		public Builder beanPropertiesWriteOnly(String beanClassName, String properties) {
278 			super.beanPropertiesWriteOnly(beanClassName, properties);
279 			return this;
280 		}
281 
282 		@Override /* Overridden from Builder */
283 		public Builder beansRequireDefaultConstructor() {
284 			super.beansRequireDefaultConstructor();
285 			return this;
286 		}
287 
288 		@Override /* Overridden from Builder */
289 		public Builder beansRequireSerializable() {
290 			super.beansRequireSerializable();
291 			return this;
292 		}
293 
294 		@Override /* Overridden from Builder */
295 		public Builder beansRequireSettersForGetters() {
296 			super.beansRequireSettersForGetters();
297 			return this;
298 		}
299 
300 		@Override /* Overridden from Context.Builder */
301 		public WriterSerializer build() {
302 			return build(WriterSerializer.class);
303 		}
304 
305 		@Override /* Overridden from Builder */
306 		public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) {
307 			super.cache(value);
308 			return this;
309 		}
310 
311 		@Override /* Overridden from Context.Builder */
312 		public Builder copy() {
313 			return new Builder(this);
314 		}
315 
316 		@Override /* Overridden from Builder */
317 		public Builder debug() {
318 			super.debug();
319 			return this;
320 		}
321 
322 		@Override /* Overridden from Builder */
323 		public Builder debug(boolean value) {
324 			super.debug(value);
325 			return this;
326 		}
327 
328 		@Override /* Overridden from Builder */
329 		public Builder detectRecursions() {
330 			super.detectRecursions();
331 			return this;
332 		}
333 
334 		@Override /* Overridden from Builder */
335 		public Builder detectRecursions(boolean value) {
336 			super.detectRecursions(value);
337 			return this;
338 		}
339 
340 		@Override /* Overridden from Builder */
341 		public Builder dictionaryOn(Class<?> on, java.lang.Class<?>...values) {
342 			super.dictionaryOn(on, values);
343 			return this;
344 		}
345 
346 		@Override /* Overridden from Builder */
347 		public Builder disableBeansRequireSomeProperties() {
348 			super.disableBeansRequireSomeProperties();
349 			return this;
350 		}
351 
352 		@Override /* Overridden from Builder */
353 		public Builder disableIgnoreMissingSetters() {
354 			super.disableIgnoreMissingSetters();
355 			return this;
356 		}
357 
358 		@Override /* Overridden from Builder */
359 		public Builder disableIgnoreTransientFields() {
360 			super.disableIgnoreTransientFields();
361 			return this;
362 		}
363 
364 		@Override /* Overridden from Builder */
365 		public Builder disableIgnoreUnknownNullBeanProperties() {
366 			super.disableIgnoreUnknownNullBeanProperties();
367 			return this;
368 		}
369 
370 		@Override /* Overridden from Builder */
371 		public Builder disableInterfaceProxies() {
372 			super.disableInterfaceProxies();
373 			return this;
374 		}
375 
376 		@Override /* Overridden from Builder */
377 		public <T> Builder example(Class<T> pojoClass, String json) {
378 			super.example(pojoClass, json);
379 			return this;
380 		}
381 
382 		@Override /* Overridden from Builder */
383 		public <T> Builder example(Class<T> pojoClass, T o) {
384 			super.example(pojoClass, o);
385 			return this;
386 		}
387 
388 		/**
389 		 * File charset.
390 		 *
391 		 * <p>
392 		 * The character set to use for writing <c>Files</c> to the file system.
393 		 *
394 		 * <p>
395 		 * Used when passing in files to {@link Serializer#serialize(Object, Object)}.
396 		 *
397 		 * <h5 class='section'>Example:</h5>
398 		 * <p class='bjava'>
399 		 * 	<jc>// Create a serializer that writes UTF-8 files.</jc>
400 		 * 	WriterSerializer <jv>serializer</jv> = JsonSerializer
401 		 * 		.<jsm>create</jsm>()
402 		 * 		.fileCharset(Charset.<jsm>forName</jsm>(<js>"UTF-8"</js>))
403 		 * 		.build();
404 		 *
405 		 * 	<jc>// Use it to read a UTF-8 encoded file.</jc>
406 		 * 	<jv>serializer</jv>.serialize(<jk>new</jk> File(<js>"MyBean.txt"</js>), <jv>myBean</jv>);
407 		 * </p>
408 		 *
409 		 * @param value
410 		 * 	The new value for this property.
411 		 * 	<br>The default is the system JVM setting.
412 		 * 	<br>Can be <jk>null</jk> (defaults to system default).
413 		 * @return This object.
414 		 */
415 		public Builder fileCharset(Charset value) {
416 			fileCharset = value;
417 			return this;
418 		}
419 
420 		@Override /* Overridden from Builder */
421 		public Builder findFluentSetters() {
422 			super.findFluentSetters();
423 			return this;
424 		}
425 
426 		@Override /* Overridden from Builder */
427 		public Builder findFluentSetters(Class<?> on) {
428 			super.findFluentSetters(on);
429 			return this;
430 		}
431 
432 		@Override /* Overridden from Context.Builder */
433 		public HashKey hashKey() {
434 			// @formatter:off
435 			return HashKey.of(
436 				super.hashKey(),
437 				fileCharset,
438 				streamCharset,
439 				maxIndent,
440 				quoteChar,
441 				quoteCharOverride,
442 				useWhitespace
443 			);
444 			// @formatter:on
445 		}
446 
447 		@Override /* Overridden from Builder */
448 		public Builder ignoreInvocationExceptionsOnGetters() {
449 			super.ignoreInvocationExceptionsOnGetters();
450 			return this;
451 		}
452 
453 		@Override /* Overridden from Builder */
454 		public Builder ignoreInvocationExceptionsOnSetters() {
455 			super.ignoreInvocationExceptionsOnSetters();
456 			return this;
457 		}
458 
459 		@Override /* Overridden from Builder */
460 		public Builder ignoreRecursions() {
461 			super.ignoreRecursions();
462 			return this;
463 		}
464 
465 		@Override /* Overridden from Builder */
466 		public Builder ignoreRecursions(boolean value) {
467 			super.ignoreRecursions(value);
468 			return this;
469 		}
470 
471 		@Override /* Overridden from Builder */
472 		public Builder ignoreUnknownBeanProperties() {
473 			super.ignoreUnknownBeanProperties();
474 			return this;
475 		}
476 
477 		@Override /* Overridden from Builder */
478 		public Builder ignoreUnknownEnumValues() {
479 			super.ignoreUnknownEnumValues();
480 			return this;
481 		}
482 
483 		@Override /* Overridden from Builder */
484 		public Builder impl(Context value) {
485 			super.impl(value);
486 			return this;
487 		}
488 
489 		@Override /* Overridden from Builder */
490 		public Builder implClass(Class<?> interfaceClass, Class<?> implClass) {
491 			super.implClass(interfaceClass, implClass);
492 			return this;
493 		}
494 
495 		@Override /* Overridden from Builder */
496 		public Builder implClasses(Map<Class<?>,Class<?>> values) {
497 			super.implClasses(values);
498 			return this;
499 		}
500 
501 		@Override /* Overridden from Builder */
502 		public Builder initialDepth(int value) {
503 			super.initialDepth(value);
504 			return this;
505 		}
506 
507 		@Override /* Overridden from Builder */
508 		public Builder interfaceClass(Class<?> on, Class<?> value) {
509 			super.interfaceClass(on, value);
510 			return this;
511 		}
512 
513 		@Override /* Overridden from Builder */
514 		public Builder interfaces(java.lang.Class<?>...value) {
515 			super.interfaces(value);
516 			return this;
517 		}
518 
519 		@Override /* Overridden from Builder */
520 		public Builder keepNullProperties() {
521 			super.keepNullProperties();
522 			return this;
523 		}
524 
525 		@Override /* Overridden from Builder */
526 		public Builder keepNullProperties(boolean value) {
527 			super.keepNullProperties(value);
528 			return this;
529 		}
530 
531 		@Override /* Overridden from Builder */
532 		public Builder listener(Class<? extends org.apache.juneau.serializer.SerializerListener> value) {
533 			super.listener(value);
534 			return this;
535 		}
536 
537 		@Override /* Overridden from Builder */
538 		public Builder locale(Locale value) {
539 			super.locale(value);
540 			return this;
541 		}
542 
543 		@Override /* Overridden from Builder */
544 		public Builder maxDepth(int value) {
545 			super.maxDepth(value);
546 			return this;
547 		}
548 
549 		/**
550 		 * Maximum indentation.
551 		 *
552 		 * <p>
553 		 * Specifies the maximum indentation level in the serialized document.
554 		 *
555 		 * <h5 class='section'>Notes:</h5><ul>
556 		 * 	<li class='note'>This setting does not apply to the RDF serializers.
557 		 * </ul>
558 		 *
559 		 * <h5 class='section'>Example:</h5>
560 		 * <p class='bjava'>
561 		 * 	<jc>// Create a serializer that indents a maximum of 20 tabs.</jc>
562 		 * 	WriterSerializer <jv>serializer</jv> = JsonSerializer
563 		 * 		.<jsm>create</jsm>()
564 		 * 		.ws()  <jc>// Enable whitespace</jc>
565 		 * 		.maxIndent(20)
566 		 * 		.build();
567 		 * </p>
568 		 *
569 		 * @param value
570 		 * 	The new value for this property.
571 		 * 	<br>The default is <c>100</c>.
572 		 * @return This object.
573 		 */
574 		public Builder maxIndent(int value) {
575 			maxIndent = value;
576 			return this;
577 		}
578 
579 		@Override /* Overridden from Builder */
580 		public Builder mediaType(MediaType value) {
581 			super.mediaType(value);
582 			return this;
583 		}
584 
585 		@Override /* Overridden from Builder */
586 		public Builder notBeanClasses(java.lang.Class<?>...values) {
587 			super.notBeanClasses(values);
588 			return this;
589 		}
590 
591 		@Override /* Overridden from Builder */
592 		public Builder notBeanPackages(String...values) {
593 			super.notBeanPackages(values);
594 			return this;
595 		}
596 
597 		@Override /* Overridden from Builder */
598 		public Builder produces(String value) {
599 			super.produces(value);
600 			return this;
601 		}
602 
603 		@Override /* Overridden from Builder */
604 		public Builder propertyNamer(Class<?> on, Class<? extends org.apache.juneau.PropertyNamer> value) {
605 			super.propertyNamer(on, value);
606 			return this;
607 		}
608 
609 		@Override /* Overridden from Builder */
610 		public Builder propertyNamer(Class<? extends org.apache.juneau.PropertyNamer> value) {
611 			super.propertyNamer(value);
612 			return this;
613 		}
614 
615 		/**
616 		 *  Quote character.
617 		 *
618 		 * <p>
619 		 * Specifies the character to use for quoting attributes and values.
620 		 *
621 		 * <h5 class='section'>Notes:</h5><ul>
622 		 * 	<li class='note'>This setting does not apply to the RDF serializers.
623 		 * </ul>
624 		 *
625 		 * <h5 class='section'>Example:</h5>
626 		 * <p class='bjava'>
627 		 * 	<jc>// Create a serializer that uses single quotes.</jc>
628 		 * 	WriterSerializer <jv>serializer</jv> = JsonSerializer
629 		 * 		.<jsm>create</jsm>()
630 		 * 		.quoteChar(<js>'\''</js>)
631 		 * 		.build();
632 		 *
633 		 * 	<jc>// A bean with a single property</jc>
634 		 * 	<jk>public class</jk> MyBean {
635 		 * 		<jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>;
636 		 * 	}
637 		 *
638 		 * 	<jc>// Produces {'foo':'bar'}</jc>
639 		 * 	String <jv>json</jv> = <jv>serializer</jv>.toString(<jk>new</jk> MyBean());
640 		 * </p>
641 		 *
642 		 * @param value
643 		 * 	The new value for this property.
644 		 * 	<br>The default is <js>'"'</js>.
645 		 * @return This object.
646 		 */
647 		public Builder quoteChar(char value) {
648 			quoteChar = value;
649 			return this;
650 		}
651 
652 		/**
653 		 * Quote character override.
654 		 *
655 		 * <p>
656 		 * Similar to {@link #quoteChar(char)} but takes precedence over that setting.
657 		 *
658 		 * <p>
659 		 * Allows you to override the quote character even if it's set by a subclass such as {@link Json5Serializer}.
660 		 *
661 		 *
662 		 * @param value
663 		 * 	The new value for this property.
664 		 * 	<br>The default is <jk>null</jk>.
665 		 * @return This object.
666 		 */
667 		public Builder quoteCharOverride(char value) {
668 			quoteCharOverride = value;
669 			return this;
670 		}
671 
672 		@Override /* Overridden from Builder */
673 		public Builder sortCollections() {
674 			super.sortCollections();
675 			return this;
676 		}
677 
678 		@Override /* Overridden from Builder */
679 		public Builder sortCollections(boolean value) {
680 			super.sortCollections(value);
681 			return this;
682 		}
683 
684 		@Override /* Overridden from Builder */
685 		public Builder sortMaps() {
686 			super.sortMaps();
687 			return this;
688 		}
689 
690 		@Override /* Overridden from Builder */
691 		public Builder sortMaps(boolean value) {
692 			super.sortMaps(value);
693 			return this;
694 		}
695 
696 		@Override /* Overridden from Builder */
697 		public Builder sortProperties() {
698 			super.sortProperties();
699 			return this;
700 		}
701 
702 		@Override /* Overridden from Builder */
703 		public Builder sortProperties(java.lang.Class<?>...on) {
704 			super.sortProperties(on);
705 			return this;
706 		}
707 
708 		/**
709 		 *  Quote character.
710 		 *
711 		 * <p>
712 		 * Specifies to use single quotes for quoting attributes and values.
713 		 *
714 		 * <h5 class='section'>Notes:</h5><ul>
715 		 * 	<li class='note'>This setting does not apply to the RDF serializers.
716 		 * </ul>
717 		 *
718 		 * <h5 class='section'>Example:</h5>
719 		 * <p class='bjava'>
720 		 * 	<jc>// Create a serializer that uses single quotes.</jc>
721 		 * 	WriterSerializer <jv>serializer</jv> = JsonSerializer
722 		 * 		.<jsm>create</jsm>()
723 		 * 		.sq()
724 		 * 		.build();
725 		 *
726 		 * 	<jc>// A bean with a single property</jc>
727 		 * 	<jk>public class</jk> MyBean {
728 		 * 		<jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>;
729 		 * 	}
730 		 *
731 		 * 	<jc>// Produces {'foo':'bar'}</jc>
732 		 * 	String <jv>json</jv> = <jv>serializer</jv>.toString(<jk>new</jk> MyBean());
733 		 * </p>
734 		 *
735 		 * @return This object.
736 		 */
737 		public Builder sq() {
738 			return quoteChar('\'');
739 		}
740 
741 		@Override /* Overridden from Builder */
742 		public Builder stopClass(Class<?> on, Class<?> value) {
743 			super.stopClass(on, value);
744 			return this;
745 		}
746 
747 		/**
748 		 * Output stream charset.
749 		 *
750 		 * <p>
751 		 * The character set to use when writing to <c>OutputStreams</c>.
752 		 *
753 		 * <p>
754 		 * Used when passing in output streams and byte arrays to {@link WriterSerializer#serialize(Object, Object)}.
755 		 *
756 		 * <h5 class='section'>Example:</h5>
757 		 * <p class='bjava'>
758 		 * 	<jc>// Create a serializer that writes UTF-8 files.</jc>
759 		 * 	WriterSerializer <jv>serializer</jv> = JsonSerializer
760 		 * 		.<jsm>create</jsm>()
761 		 * 		.streamCharset(Charset.<jsm>forName</jsm>(<js>"UTF-8"</js>))
762 		 * 		.build();
763 		 *
764 		 * 	<jc>// Use it to write to a UTF-8 encoded output stream.</jc>
765 		 * 	<jv>serializer</jv>.serializer(<jk>new</jk> FileOutputStreamStream(<js>"MyBean.txt"</js>), <jv>myBean</jv>);
766 		 * </p>
767 		 *
768 		 * @param value
769 		 * 	The new value for this property.
770 		 * 	<br>The default is the system JVM setting.
771 		 * 	<br>Can be <jk>null</jk> (defaults to UTF-8).
772 		 * @return This object.
773 		 */
774 		public Builder streamCharset(Charset value) {
775 			streamCharset = value;
776 			return this;
777 		}
778 
779 		@Override /* Overridden from Builder */
780 		public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction) {
781 			super.swap(normalClass, swappedClass, swapFunction);
782 			return this;
783 		}
784 
785 		@Override /* Overridden from Builder */
786 		public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction, ThrowingFunction<S,T> unswapFunction) {
787 			super.swap(normalClass, swappedClass, swapFunction, unswapFunction);
788 			return this;
789 		}
790 
791 		@Override /* Overridden from Builder */
792 		public Builder swaps(Class<?>...values) {
793 			super.swaps(values);
794 			return this;
795 		}
796 
797 		@Override /* Overridden from Builder */
798 		public Builder swaps(Object...values) {
799 			super.swaps(values);
800 			return this;
801 		}
802 
803 		@Override /* Overridden from Builder */
804 		public Builder timeZone(TimeZone value) {
805 			super.timeZone(value);
806 			return this;
807 		}
808 
809 		@Override /* Overridden from Builder */
810 		public Builder trimEmptyCollections() {
811 			super.trimEmptyCollections();
812 			return this;
813 		}
814 
815 		@Override /* Overridden from Builder */
816 		public Builder trimEmptyCollections(boolean value) {
817 			super.trimEmptyCollections(value);
818 			return this;
819 		}
820 
821 		@Override /* Overridden from Builder */
822 		public Builder trimEmptyMaps() {
823 			super.trimEmptyMaps();
824 			return this;
825 		}
826 
827 		@Override /* Overridden from Builder */
828 		public Builder trimEmptyMaps(boolean value) {
829 			super.trimEmptyMaps(value);
830 			return this;
831 		}
832 
833 		@Override /* Overridden from Builder */
834 		public Builder trimStrings() {
835 			super.trimStrings();
836 			return this;
837 		}
838 
839 		@Override /* Overridden from Builder */
840 		public Builder trimStrings(boolean value) {
841 			super.trimStrings(value);
842 			return this;
843 		}
844 
845 		@Override /* Overridden from Builder */
846 		public Builder type(Class<? extends org.apache.juneau.Context> value) {
847 			super.type(value);
848 			return this;
849 		}
850 
851 		@Override /* Overridden from Builder */
852 		public Builder typeName(Class<?> on, String value) {
853 			super.typeName(on, value);
854 			return this;
855 		}
856 
857 		@Override /* Overridden from Builder */
858 		public Builder typePropertyName(Class<?> on, String value) {
859 			super.typePropertyName(on, value);
860 			return this;
861 		}
862 
863 		@Override /* Overridden from Builder */
864 		public Builder typePropertyName(String value) {
865 			super.typePropertyName(value);
866 			return this;
867 		}
868 
869 		@Override /* Overridden from Builder */
870 		public Builder uriContext(UriContext value) {
871 			super.uriContext(value);
872 			return this;
873 		}
874 
875 		@Override /* Overridden from Builder */
876 		public Builder uriRelativity(UriRelativity value) {
877 			super.uriRelativity(value);
878 			return this;
879 		}
880 
881 		@Override /* Overridden from Builder */
882 		public Builder uriResolution(UriResolution value) {
883 			super.uriResolution(value);
884 			return this;
885 		}
886 
887 		@Override /* Overridden from Builder */
888 		public Builder useEnumNames() {
889 			super.useEnumNames();
890 			return this;
891 		}
892 
893 		@Override /* Overridden from Builder */
894 		public Builder useJavaBeanIntrospector() {
895 			super.useJavaBeanIntrospector();
896 			return this;
897 		}
898 
899 		/**
900 		 *  Use whitespace.
901 		 *
902 		 * <p>
903 		 * When enabled, whitespace is added to the output to improve readability.
904 		 *
905 		 * <h5 class='section'>Example:</h5>
906 		 * <p class='bjava'>
907 		 * 	<jc>// Create a serializer with whitespace enabled.</jc>
908 		 * 	WriterSerializer <jv>serializer</jv> = JsonSerializer
909 		 * 		.<jsm>create</jsm>()
910 		 * 		.useWhitespace()
911 		 * 		.build();
912 		 *
913 		 * 	<jc>// A bean with a single property</jc>
914 		 * 	<jk>public class</jk> MyBean {
915 		 * 		<jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>;
916 		 * 	}
917 		 *
918 		 * 	<jc>// Produces "\{\n\t"foo": "bar"\n\}\n"</jc>
919 		 * 	String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> MyBean());
920 		 * </p>
921 		 *
922 		 * @return This object.
923 		 */
924 		public Builder useWhitespace() {
925 			return useWhitespace(true);
926 		}
927 
928 		/**
929 		 * Same as {@link #useWhitespace()} but allows you to explicitly specify the value.
930 		 *
931 		 * @param value The value for this setting.
932 		 * @return This object.
933 		 */
934 		public Builder useWhitespace(boolean value) {
935 			useWhitespace = value;
936 			return this;
937 		}
938 
939 		/**
940 		 *  Use whitespace.
941 		 *
942 		 * <p>
943 		 * When enabled, whitespace is added to the output to improve readability.
944 		 *
945 		 * <h5 class='section'>Example:</h5>
946 		 * <p class='bjava'>
947 		 * 	<jc>// Create a serializer with whitespace enabled.</jc>
948 		 * 	WriterSerializer <jv>serializer</jv> = JsonSerializer
949 		 * 		.<jsm>create</jsm>()
950 		 * 		.ws()
951 		 * 		.build();
952 		 *
953 		 * 	<jc>// A bean with a single property</jc>
954 		 * 	<jk>public class</jk> MyBean {
955 		 * 		<jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>;
956 		 * 	}
957 		 *
958 		 * 	<jc>// Produces "\{\n\t"foo": "bar"\n\}\n"</jc>
959 		 * 	String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> MyBean());
960 		 * </p>
961 		 *
962 		 * @return This object.
963 		 */
964 		public Serializer.Builder ws() {
965 			return useWhitespace();
966 		}
967 	}
968 
969 	/**
970 	 * Creates a new builder for this object.
971 	 *
972 	 * @return A new builder.
973 	 */
974 	public static Builder create() {
975 		return new Builder();
976 	}
977 
978 	protected final boolean useWhitespace;
979 	protected final Character quoteChar;
980 	protected final Character quoteCharOverride;
981 	private final Charset fileCharset;
982 	private final Charset streamCharset;
983 	protected final int maxIndent;
984 
985 	private final char quoteCharValue;
986 
987 	/**
988 	 * Constructor.
989 	 *
990 	 * @param builder
991 	 * 	The builder for this object.
992 	 */
993 	protected WriterSerializer(Builder builder) {
994 		super(builder);
995 
996 		fileCharset = builder.fileCharset;
997 		maxIndent = builder.maxIndent;
998 		quoteChar = builder.quoteChar;
999 		quoteCharOverride = builder.quoteCharOverride;
1000 		streamCharset = builder.streamCharset;
1001 		useWhitespace = builder.useWhitespace;
1002 
1003 		quoteCharValue = nn(quoteCharOverride) ? quoteCharOverride : nn(quoteChar) ? quoteChar : '"';
1004 	}
1005 
1006 	@Override /* Overridden from Context */
1007 	public Builder copy() {
1008 		return new Builder(this);
1009 	}
1010 
1011 	@Override /* Overridden from Context */
1012 	public WriterSerializerSession.Builder createSession() {
1013 		return WriterSerializerSession.create(this);
1014 	}
1015 
1016 	@Override /* Overridden from Context */
1017 	public WriterSerializerSession getSession() { return createSession().build(); }
1018 
1019 	@Override /* Overridden from Serializer */
1020 	public final boolean isWriterSerializer() { return true; }
1021 
1022 	/**
1023 	 * Convenience method for serializing an object and sending it to STDOUT.
1024 	 *
1025 	 * @param o The object to serialize.
1026 	 * @return This object.
1027 	 */
1028 	public final WriterSerializer println(Object o) {
1029 		System.out.println(toString(o));  // NOT DEBUG
1030 		return this;
1031 	}
1032 
1033 	/**
1034 	 * Convenience method for serializing an object to a <c>String</c>.
1035 	 *
1036 	 * @param o The object to serialize.
1037 	 * @return The output serialized to a string.
1038 	 * @throws SerializeException If a problem occurred trying to convert the output.
1039 	 */
1040 	@Override /* Overridden from Serializer */
1041 	public final String serialize(Object o) throws SerializeException {
1042 		return getSession().serialize(o);
1043 	}
1044 
1045 	/**
1046 	 * Identical to {@link #serialize(Object)} except throws a {@link RuntimeException} instead of a {@link SerializeException}.
1047 	 *
1048 	 * <p>
1049 	 * This is typically good enough for debugging purposes.
1050 	 *
1051 	 * @param o The object to serialize.
1052 	 * @return The serialized object.
1053 	 */
1054 	public final String toString(Object o) {
1055 		try {
1056 			return serialize(o);
1057 		} catch (Exception e) {
1058 			throw toRex(e);
1059 		}
1060 	}
1061 
1062 	/**
1063 	 * File charset.
1064 	 *
1065 	 * @see Builder#fileCharset(Charset)
1066 	 * @return
1067 	 * 	The character set to use when writing to <c>Files</c> on the file system.
1068 	 */
1069 	protected final Charset getFileCharset() { return fileCharset; }
1070 
1071 	/**
1072 	 * Maximum indentation.
1073 	 *
1074 	 * @see Builder#maxIndent(int)
1075 	 * @return
1076 	 * 	The maximum indentation level in the serialized document.
1077 	 */
1078 	protected final int getMaxIndent() { return maxIndent; }
1079 
1080 	/**
1081 	 * Quote character.
1082 	 *
1083 	 * @see Builder#quoteChar(char)
1084 	 * @return
1085 	 * 	The character used for quoting attributes and values.
1086 	 */
1087 	protected char getQuoteChar() { return quoteCharValue; }
1088 
1089 	/**
1090 	 * Output stream charset.
1091 	 *
1092 	 * @see Builder#streamCharset(Charset)
1093 	 * @return
1094 	 * 	The character set to use when writing to <c>OutputStreams</c> and byte arrays.
1095 	 */
1096 	protected final Charset getStreamCharset() { return streamCharset; }
1097 
1098 	/**
1099 	 * Trim strings.
1100 	 *
1101 	 * @see Builder#useWhitespace()
1102 	 * @return
1103 	 * 	When enabled, whitespace is added to the output to improve readability.
1104 	 */
1105 	protected final boolean isUseWhitespace() { return useWhitespace; }
1106 
1107 	@Override /* Overridden from Serializer */
1108 	protected FluentMap<String,Object> properties() {
1109 		return super.properties()
1110 			.a("fileCharset", fileCharset)
1111 			.a("maxIndent", maxIndent)
1112 			.a("quoteChar", quoteChar)
1113 			.a("streamCharset", streamCharset)
1114 			.a("useWhitespace", useWhitespace);
1115 	}
1116 
1117 	/**
1118 	 * Quote character.
1119 	 *
1120 	 * @see Builder#quoteChar(char)
1121 	 * @return
1122 	 * 	The character used for quoting attributes and values.
1123 	 */
1124 	protected Character quoteChar() {
1125 		return nn(quoteCharOverride) ? quoteCharOverride : quoteChar;
1126 	}
1127 }