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 java.nio.charset.*; 016 017import org.apache.juneau.*; 018import org.apache.juneau.annotation.*; 019import org.apache.juneau.internal.*; 020import org.apache.juneau.utils.*; 021 022/** 023 * Subclass of {@link Serializer} for character-based serializers. 024 */ 025@ConfigurableContext 026public abstract class WriterSerializer extends Serializer { 027 028 //------------------------------------------------------------------------------------------------------------------- 029 // Configurable properties 030 //------------------------------------------------------------------------------------------------------------------- 031 032 static final String PREFIX = "WriterSerializer"; 033 034 /** 035 * Configuration property: File charset. 036 * 037 * <h5 class='section'>Property:</h5> 038 * <ul class='spaced-list'> 039 * <li><b>ID:</b> {@link org.apache.juneau.serializer.WriterSerializer#WSERIALIZER_fileCharset WSERIALIZER_fileCharset} 040 * <li><b>Name:</b> <js>"WriterSerializer.fileCharset.s"</js> 041 * <li><b>Data type:</b> <c>String</c> 042 * <li><b>System property:</b> <c>WriterSerializer.fileCharset</c> 043 * <li><b>Environment variable:</b> <c>WRITERSERIALIZER_FILECHARSET</c> 044 * <li><b>Default:</b> <js>"DEFAULT"</js> 045 * <li><b>Session property:</b> <jk>false</jk> 046 * <li><b>Annotations:</b> 047 * <ul> 048 * <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#fileCharset()} 049 * </ul> 050 * <li><b>Methods:</b> 051 * <ul> 052 * <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializerBuilder#fileCharset(Charset)} 053 * </ul> 054 * </ul> 055 * 056 * <h5 class='section'>Description:</h5> 057 * <p> 058 * The character set to use for writing <c>Files</c> to the file system. 059 * 060 * <p> 061 * Used when passing in files to {@link Serializer#serialize(Object, Object)}. 062 * 063 * <p> 064 * <js>"DEFAULT"</js> can be used to indicate the JVM default file system charset. 065 * 066 * <h5 class='section'>Example:</h5> 067 * <p class='bcode w800'> 068 * <jc>// Create a serializer that writes UTF-8 files.</jc> 069 * WriterSerializer s = JsonSerializer. 070 * .<jsm>create</jsm>() 071 * .fileCharset(Charset.<jsm>forName</jsm>(<js>"UTF-8"</js>)) 072 * .build(); 073 * 074 * <jc>// Same, but use property.</jc> 075 * WriterSerializer s = JsonSerializer. 076 * .<jsm>create</jsm>() 077 * .set(<jsf>WSERIALIZER_fileCharset</jsf>, <js>"UTF-8"</js>) 078 * .build(); 079 * 080 * <jc>// Use it to read a UTF-8 encoded file.</jc> 081 * s.serialize(<jk>new</jk> File(<js>"MyBean.txt"</js>), myBean); 082 * </p> 083 */ 084 public static final String WSERIALIZER_fileCharset = PREFIX + ".fileCharset.s"; 085 086 /** 087 * Configuration property: Maximum indentation. 088 * 089 * <h5 class='section'>Property:</h5> 090 * <ul class='spaced-list'> 091 * <li><b>ID:</b> {@link org.apache.juneau.serializer.WriterSerializer#WSERIALIZER_maxIndent WSERIALIZER_maxIndent} 092 * <li><b>Name:</b> <js>"WriterSerializer.maxIndent.i"</js> 093 * <li><b>Data type:</b> <jk>int</jk> 094 * <li><b>System property:</b> <c>WriterSerializer.maxIndent</c> 095 * <li><b>Environment variable:</b> <c>WRITERSERIALIZER_MAXINDENT</c> 096 * <li><b>Default:</b> <c>100</c> 097 * <li><b>Session property:</b> <jk>false</jk> 098 * <li><b>Annotations:</b> 099 * <ul> 100 * <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#maxIndent()} 101 * </ul> 102 * <li><b>Methods:</b> 103 * <ul> 104 * <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializerBuilder#maxIndent(int)} 105 * </ul> 106 * </ul> 107 * 108 * <h5 class='section'>Description:</h5> 109 * <p> 110 * Specifies the maximum indentation level in the serialized document. 111 * 112 * <p> 113 * This setting does not apply to the RDF serializers. 114 * 115 * <h5 class='section'>Example:</h5> 116 * <p class='bcode w800'> 117 * <jc>// Create a serializer that indents a maximum of 20 tabs.</jc> 118 * WriterSerializer s = JsonSerializer 119 * .<jsm>create</jsm>() 120 * .maxIndent(20) 121 * .build(); 122 * 123 * <jc>// Same, but use property.</jc> 124 * WriterSerializer s = JsonSerializer 125 * .<jsm>create</jsm>() 126 * .set(<jsf>SERIALIZER_maxIndent</jsf>, 20) 127 * .build(); 128 * </p> 129 */ 130 public static final String WSERIALIZER_maxIndent = PREFIX + ".maxIndent.i"; 131 132 /** 133 * Configuration property: Quote character. 134 * 135 * <h5 class='section'>Property:</h5> 136 * <ul class='spaced-list'> 137 * <li><b>ID:</b> {@link org.apache.juneau.serializer.WriterSerializer#WSERIALIZER_quoteChar WSERIALIZER_quoteChar} 138 * <li><b>Name:</b> <js>"WriterSerializer.quoteChar.s"</js> 139 * <li><b>Data type:</b> <c>String</c> 140 * <li><b>System property:</b> <c>WriterSerializer.quoteChar</c> 141 * <li><b>Environment variable:</b> <c>WRITERSERIALIZER_QUOTECHAR</c> 142 * <li><b>Default:</b> <js>"\""</js> 143 * <li><b>Session property:</b> <jk>false</jk> 144 * <li><b>Annotations:</b> 145 * <ul> 146 * <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#quoteChar()} 147 * </ul> 148 * <li><b>Methods:</b> 149 * <ul> 150 * <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializerBuilder#quoteChar(char)} 151 * <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializerBuilder#sq()} 152 * </ul> 153 * </ul> 154 * 155 * <h5 class='section'>Description:</h5> 156 * <p> 157 * This is the character used for quoting attributes and values. 158 * 159 * <p> 160 * This setting does not apply to the RDF serializers. 161 * 162 * <h5 class='section'>Example:</h5> 163 * <p class='bcode w800'> 164 * <jc>// Create a serializer that uses single quotes.</jc> 165 * WriterSerializer s = JsonSerializer 166 * .<jsm>create</jsm>() 167 * .sq() 168 * .build(); 169 * 170 * <jc>// Same, but use property.</jc> 171 * WriterSerializer s = JsonSerializer 172 * .<jsm>create</jsm>() 173 * .set(<jsf>WSERIALIZER_quoteChar</jsf>, <js>'\''</js>) 174 * .build(); 175 * </p> 176 */ 177 public static final String WSERIALIZER_quoteChar = PREFIX + ".quoteChar.s"; 178 179 /** 180 * Configuration property: Output stream charset. 181 * 182 * <h5 class='section'>Property:</h5> 183 * <ul class='spaced-list'> 184 * <li><b>ID:</b> {@link org.apache.juneau.serializer.WriterSerializer#WSERIALIZER_streamCharset WSERIALIZER_streamCharset} 185 * <li><b>Name:</b> <js>"WriterSerializer.streamCharset.s"</js> 186 * <li><b>Data type:</b> <c>String</c> 187 * <li><b>System property:</b> <c>WriterSerializer.streamCharset</c> 188 * <li><b>Environment variable:</b> <c>WRITERSERIALIZER_STREAMCHARSET</c> 189 * <li><b>Default:</b> <js>"UTF-8"</js> 190 * <li><b>Session property:</b> <jk>false</jk> 191 * <li><b>Annotations:</b> 192 * <ul> 193 * <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#streamCharset()} 194 * </ul> 195 * <li><b>Methods:</b> 196 * <ul> 197 * <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializerBuilder#streamCharset(Charset)} 198 * </ul> 199 * </ul> 200 * 201 * <h5 class='section'>Description:</h5> 202 * <p> 203 * The character set to use when writing to <c>OutputStreams</c>. 204 * 205 * <p> 206 * Used when passing in output streams and byte arrays to {@link WriterSerializer#serialize(Object, Object)}. 207 * 208 * <h5 class='section'>Example:</h5> 209 * <p class='bcode w800'> 210 * <jc>// Create a serializer that writes UTF-8 files.</jc> 211 * WriterSerializer s = JsonSerializer. 212 * .<jsm>create</jsm>() 213 * .streamCharset(Charset.<jsm>forName</jsm>(<js>"UTF-8"</js>)) 214 * .build(); 215 * 216 * <jc>// Same, but use property.</jc> 217 * WriterSerializer s = JsonSerializer. 218 * .<jsm>create</jsm>() 219 * .set(<jsf>WSERIALIZER_streamCharset</jsf>, <js>"UTF-8"</js>) 220 * .build(); 221 * 222 * <jc>// Use it to write to a UTF-8 encoded output stream.</jc> 223 * s.serializer(<jk>new</jk> FileOutputStreamStream(<js>"MyBean.txt"</js>), myBean); 224 * </p> 225 */ 226 public static final String WSERIALIZER_streamCharset = PREFIX + ".streamCharset.s"; 227 228 /** 229 * Configuration property: Use whitespace. 230 * 231 * <h5 class='section'>Property:</h5> 232 * <ul class='spaced-list'> 233 * <li><b>ID:</b> {@link org.apache.juneau.serializer.WriterSerializer#WSERIALIZER_useWhitespace WSERIALIZER_useWhitespace} 234 * <li><b>Name:</b> <js>"WriterSerializer.useWhitespace.b"</js> 235 * <li><b>Data type:</b> <jk>boolean</jk> 236 * <li><b>System property:</b> <c>WriterSerializer.useWhitespace</c> 237 * <li><b>Environment variable:</b> <c>WRITERSERIALIZER_USEWHITESPACE</c> 238 * <li><b>Default:</b> <jk>false</jk> 239 * <li><b>Session property:</b> <jk>true</jk> 240 * <li><b>Annotations:</b> 241 * <ul> 242 * <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#useWhitespace()} 243 * </ul> 244 * <li><b>Methods:</b> 245 * <ul> 246 * <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializerBuilder#useWhitespace(boolean)} 247 * <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializerBuilder#useWhitespace()} 248 * <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializerBuilder#ws()} 249 * </ul> 250 * </ul> 251 * 252 * <h5 class='section'>Description:</h5> 253 * <p> 254 * If <jk>true</jk>, whitespace is added to the output to improve readability. 255 * 256 * <h5 class='section'>Example:</h5> 257 * <p class='bcode w800'> 258 * <jc>// Create a serializer with whitespace enabled.</jc> 259 * WriterSerializer s = JsonSerializer 260 * .<jsm>create</jsm>() 261 * .ws() 262 * .build(); 263 * 264 * <jc>// Same, but use property.</jc> 265 * WriterSerializer s = JsonSerializer 266 * .<jsm>create</jsm>() 267 * .set(<jsf>WSERIALIZER_useWhitespace</jsf>, <jk>true</jk>) 268 * .build(); 269 * 270 * <jc>// Produces "\{\n\t'foo': 'bar'\n\}\n"</jc> 271 * String json = s.serialize(<jk>new</jk> MyBean()); 272 * </p> 273 */ 274 public static final String WSERIALIZER_useWhitespace = PREFIX + ".useWhitespace.b"; 275 276 static final WriterSerializer DEFAULT = new WriterSerializer(PropertyStore.create().build(), "", "") { 277 @Override 278 public WriterSerializerSession createSession(SerializerSessionArgs args) { 279 throw new NoSuchMethodError(); 280 } 281 }; 282 283 //------------------------------------------------------------------------------------------------------------------- 284 // Instance 285 //------------------------------------------------------------------------------------------------------------------- 286 287 private final Charset fileCharset; 288 private final int maxIndent; 289 private final char quoteChar; 290 private final Charset streamCharset; 291 private final boolean useWhitespace; 292 293 /** 294 * Constructor. 295 * 296 * @param ps 297 * The property store containing all the settings for this object. 298 * @param produces 299 * The media type that this serializer produces. 300 * @param accept 301 * The accept media types that the serializer can handle. 302 * <p> 303 * Can contain meta-characters per the <c>media-type</c> specification of {@doc RFC2616.section14.1} 304 * <p> 305 * If empty, then assumes the only media type supported is <c>produces</c>. 306 * <p> 307 * For example, if this serializer produces <js>"application/json"</js> but should handle media types of 308 * <js>"application/json"</js> and <js>"text/json"</js>, then the arguments should be: 309 * <p class='bcode w800'> 310 * <jk>super</jk>(ps, <js>"application/json"</js>, <js>"application/json,text/json"</js>); 311 * </p> 312 * <br>...or... 313 * <p class='bcode w800'> 314 * <jk>super</jk>(ps, <js>"application/json"</js>, <js>"*​/json"</js>); 315 * </p> 316 * <p> 317 * The accept value can also contain q-values. 318 */ 319 protected WriterSerializer(PropertyStore ps, String produces, String accept) { 320 super(ps, produces, accept); 321 322 maxIndent = getIntegerProperty(WSERIALIZER_maxIndent, 100); 323 quoteChar = getStringProperty(WSERIALIZER_quoteChar, "\"").charAt(0); 324 streamCharset = getProperty(WSERIALIZER_streamCharset, Charset.class, IOUtils.UTF8); 325 fileCharset = getProperty(WSERIALIZER_fileCharset, Charset.class, Charset.defaultCharset()); 326 useWhitespace = getBooleanProperty(WSERIALIZER_useWhitespace, false); 327 } 328 329 //----------------------------------------------------------------------------------------------------------------- 330 // Abstract methods 331 //----------------------------------------------------------------------------------------------------------------- 332 333 @Override /* SerializerSession */ 334 public abstract WriterSerializerSession createSession(SerializerSessionArgs args); 335 336 //----------------------------------------------------------------------------------------------------------------- 337 // Other methods 338 //----------------------------------------------------------------------------------------------------------------- 339 340 @Override /* Context */ 341 public WriterSerializerSession createSession() { 342 return createSession(createDefaultSessionArgs()); 343 } 344 345 @Override /* Serializer */ 346 public final boolean isWriterSerializer() { 347 return true; 348 } 349 350 /** 351 * Convenience method for serializing an object to a <c>String</c>. 352 * 353 * @param o The object to serialize. 354 * @return The output serialized to a string. 355 * @throws SerializeException If a problem occurred trying to convert the output. 356 */ 357 @Override /* Serializer */ 358 public final String serialize(Object o) throws SerializeException { 359 return createSession(createDefaultSessionArgs()).serialize(o); 360 } 361 362 /** 363 * Identical to {@link #serialize(Object)} except throws a {@link RuntimeException} instead of a {@link SerializeException}. 364 * 365 * <p> 366 * This is typically good enough for debugging purposes. 367 * 368 * @param o The object to serialize. 369 * @return The serialized object. 370 */ 371 public final String toString(Object o) { 372 try { 373 return serialize(o); 374 } catch (Exception e) { 375 throw new RuntimeException(e); 376 } 377 } 378 379 /** 380 * Wraps the specified object inside a {@link StringObject}. 381 * 382 * @param o The object to wrap. 383 * @return The wrapped object. 384 */ 385 public final StringObject toStringObject(Object o) { 386 return new StringObject(this, o); 387 } 388 389 /** 390 * Convenience method for serializing an object and sending it to STDOUT. 391 * 392 * @param o The object to serialize. 393 * @return This object (for method chaining). 394 */ 395 public final WriterSerializer println(Object o) { 396 System.out.println(toString(o)); // NOT DEBUG 397 return this; 398 } 399 400 //----------------------------------------------------------------------------------------------------------------- 401 // Properties 402 //----------------------------------------------------------------------------------------------------------------- 403 404 /** 405 * Configuration property: File charset. 406 * 407 * @see #WSERIALIZER_fileCharset 408 * @return 409 * The character set to use when writing to <c>Files</c> on the file system. 410 */ 411 protected final Charset getFileCharset() { 412 return fileCharset; 413 } 414 415 /** 416 * Configuration property: Maximum indentation. 417 * 418 * @see #WSERIALIZER_maxIndent 419 * @return 420 * The maximum indentation level in the serialized document. 421 */ 422 protected final int getMaxIndent() { 423 return maxIndent; 424 } 425 426 /** 427 * Configuration property: Quote character. 428 * 429 * @see #WSERIALIZER_quoteChar 430 * @return 431 * The character used for quoting attributes and values. 432 */ 433 protected final char getQuoteChar() { 434 return quoteChar; 435 } 436 437 /** 438 * Configuration property: Output stream charset. 439 * 440 * @see #WSERIALIZER_streamCharset 441 * @return 442 * The character set to use when writing to <c>OutputStreams</c> and byte arrays. 443 */ 444 protected final Charset getStreamCharset() { 445 return streamCharset; 446 } 447 448 /** 449 * Configuration property: Trim strings. 450 * 451 * @see #WSERIALIZER_useWhitespace 452 * @return 453 * If <jk>true</jk>, whitespace is added to the output to improve readability. 454 */ 455 protected final boolean isUseWhitespace() { 456 return useWhitespace; 457 } 458 459 //----------------------------------------------------------------------------------------------------------------- 460 // Other methods 461 //----------------------------------------------------------------------------------------------------------------- 462 463 @Override /* Context */ 464 public ObjectMap toMap() { 465 return super.toMap() 466 .append("WriterSerializer", new DefaultFilteringObjectMap() 467 .append("fileCharset", fileCharset) 468 .append("maxIndent", maxIndent) 469 .append("quoteChar", quoteChar) 470 .append("streamCharset", streamCharset) 471 .append("useWhitespace", useWhitespace) 472 ); 473 } 474}