001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.marshall.marshaller; 018 019import org.apache.juneau.marshall.json5.*; 020import java.io.*; 021import java.lang.reflect.*; 022import org.apache.juneau.marshall.parser.*; 023import org.apache.juneau.marshall.serializer.*; 024import org.apache.juneau.marshall.stream.*; 025 026/** 027 * A pairing of a {@link Json5Serializer} and {@link Json5Parser} into a single class with convenience read/write methods. 028 * 029 * <p> 030 * The general idea is to combine a single serializer and parser inside a simplified API for reading and writing POJOs. 031 * 032 * <h5 class='figure'>Examples:</h5> 033 * <p class='bjava'> 034 * <jc>// Using instance.</jc> 035 * Json5 <jv>json</jv> = <jk>new</jk> Json5(); 036 * MyPojo <jv>myPojo</jv> = <jv>json</jv>.read(<jv>string</jv>, MyPojo.<jk>class</jk>); 037 * String <jv>string</jv> = <jv>json</jv>.write(<jv>myPojo</jv>); 038 * </p> 039 * <p class='bjava'> 040 * <jc>// Using DEFAULT instance.</jc> 041 * MyPojo <jv>myPojo</jv> = Json5.<jsf>DEFAULT</jsf>.read(<jv>string</jv>, MyPojo.<jk>class</jk>); 042 * String <jv>string</jv> = Json5.<jsf>DEFAULT</jsf>.write(<jv>myPojo</jv>); 043 * </p> 044 * 045 * <h5 class='figure'>Example output (Map of name/age, JSON5 allows unquoted keys):</h5> 046 * <p class='bjson'> 047 * {name:<js>"Alice"</js>,age:30} 048 * </p> 049 * 050 * <h5 class='figure'>Complex (nested object + array):</h5> 051 * <p class='bjson'> 052 * {name:<js>"Alice"</js>,age:30,address:{street:<js>"123 Main St"</js>,city:<js>"Boston"</js>,state:<js>"MA"</js>},tags:[<js>"a"</js>,<js>"b"</js>,<js>"c"</js>]} 053 * </p> 054 * 055 * <p class='bjava'> 056 * <jc>// Using static shortcuts.</jc> 057 * MyPojo <jv>myPojo</jv> = Json5.<jsm>to</jsm>(<jv>string</jv>, MyPojo.<jk>class</jk>); 058 * String <jv>string</jv> = Json5.<jsm>of</jsm>(<jv>myPojo</jv>); 059 * </p> 060 * 061 * <h5 class='section'>See Also:</h5><ul> 062 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshallers">Marshallers</a> 063 * </ul> 064 */ 065@SuppressWarnings({ 066 "resource" // Cursor shortcut methods return Closeables owned by the caller; Eclipse JDT @Owning warning is by design. 067}) 068public class Json5 extends CharMarshaller { 069 070 /** 071 * Default reusable instance. 072 */ 073 public static final Json5 DEFAULT = new Json5(); 074 075 /** 076 * Serializes a POJO to a <c>String</c> using the {@link #DEFAULT} marshaller. 077 * 078 * <p> 079 * A shortcut for calling <c><jsf>DEFAULT</jsf>.write(<jv>object</jv>)</c>. 080 * 081 * @param object The object to serialize. 082 * @return The serialized object. 083 * @throws SerializeException If a problem occurred trying to convert the output. 084 */ 085 public static String of(Object object) throws SerializeException { 086 return DEFAULT.write(object); 087 } 088 089 /** 090 * Parses an input into the specified object type using the {@link #DEFAULT} marshaller. 091 * 092 * <p> 093 * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, <jv>type</jv>)</c>. 094 * 095 * @param <T> The class type of the object being created. 096 * @param input The input. 097 * @param type The object type to create. 098 * @return The parsed object. 099 * @throws ParseException Malformed input encountered. 100 */ 101 public static <T> T to(String input, Class<T> type) throws ParseException { 102 return DEFAULT.read(input, type); 103 } 104 105 /** 106 * Parses an input into the specified parameterized object type using the {@link #DEFAULT} marshaller. 107 * 108 * <p> 109 * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, <jv>type</jv>, <jv>args</jv>)</c>. 110 * 111 * @param <T> The class type of the object to create. 112 * @param input The input. 113 * @param type The object type to create. 114 * @param args The type arguments of the class if it's a collection or map. 115 * @return The parsed object. 116 * @throws ParseException Malformed input encountered. 117 */ 118 public static <T> T to(String input, Type type, Type... args) throws ParseException { 119 return DEFAULT.read(input, type, args); 120 } 121 122 /** 123 * Parses the contents of a {@link Reader} into the specified object type using the {@link #DEFAULT} marshaller. 124 * 125 * <p> 126 * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, <jv>type</jv>)</c> that catches any 127 * {@link IOException} from the underlying stream and rethrows it as an unchecked {@link ParseException}, so the 128 * caller is not burdened with a checked exception. 129 * 130 * @param <T> The class type of the object being created. 131 * @param input The input reader. 132 * @param type The object type to create. 133 * @return The parsed object. 134 * @throws ParseException Malformed input encountered or an I/O error occurred on the underlying stream. 135 */ 136 public static <T> T to(Reader input, Class<T> type) throws ParseException { 137 try { 138 return DEFAULT.read(input, type); 139 } catch (IOException e) { 140 throw new ParseException(e); 141 } 142 } 143 144 /** 145 * Parses the contents of a {@link Reader} into the specified parameterized object type using the {@link #DEFAULT} marshaller. 146 * 147 * <p> 148 * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, <jv>type</jv>, <jv>args</jv>)</c> that catches any 149 * {@link IOException} from the underlying stream and rethrows it as an unchecked {@link ParseException}, so the 150 * caller is not burdened with a checked exception. 151 * 152 * @param <T> The class type of the object to create. 153 * @param input The input reader. 154 * @param type The object type to create. 155 * @param args The type arguments of the class if it's a collection or map. 156 * @return The parsed object. 157 * @throws ParseException Malformed input encountered or an I/O error occurred on the underlying stream. 158 */ 159 public static <T> T to(Reader input, Type type, Type... args) throws ParseException { 160 try { 161 return DEFAULT.read(input, type, args); 162 } catch (IOException e) { 163 throw new ParseException(e); 164 } 165 } 166 167 /** 168 * Serializes a POJO to the specified {@link Writer} using the {@link #DEFAULT} marshaller. 169 * 170 * <p> 171 * A shortcut for calling <c><jsf>DEFAULT</jsf>.write(<jv>object</jv>, <jv>output</jv>)</c> that catches any 172 * {@link IOException} from the underlying stream and rethrows it as an unchecked {@link SerializeException}, so the 173 * caller is not burdened with a checked exception. 174 * 175 * @param object The object to serialize. 176 * @param output The writer to serialize to. 177 * @throws SerializeException If a problem occurred trying to convert the output or an I/O error occurred on the underlying stream. 178 */ 179 public static void of(Object object, Writer output) throws SerializeException { 180 try { 181 DEFAULT.write(object, output); 182 } catch (IOException e) { 183 throw new SerializeException(e); 184 } 185 } 186 187 /** 188 * Opens a low-level {@link TokenReader} cursor over the specified input using the {@link #DEFAULT} marshaller. 189 * 190 * <p> 191 * A shortcut for calling <c><jsf>DEFAULT</jsf>.readTokens(<jv>input</jv>)</c>. 192 * 193 * @param input The input. 194 * @return A new {@link TokenReader}. 195 * @throws IOException If a problem occurred opening the underlying input. 196 */ 197 public static TokenReader toTokens(Object input) throws IOException { 198 return DEFAULT.readTokens(input); 199 } 200 201 /** 202 * Opens a low-level {@link TokenWriter} generator over the specified output using the {@link #DEFAULT} marshaller. 203 * 204 * <p> 205 * A shortcut for calling <c><jsf>DEFAULT</jsf>.writeTokens(<jv>output</jv>)</c>. 206 * 207 * @param output The output. 208 * @return A new {@link TokenWriter}. 209 * @throws IOException If a problem occurred opening the underlying output. 210 */ 211 public static TokenWriter ofTokens(Object output) throws IOException { 212 return DEFAULT.writeTokens(output); 213 } 214 215 /** 216 * Opens a low-level {@link RecordReader} cursor over the specified input using the {@link #DEFAULT} marshaller. 217 * 218 * <p> 219 * A shortcut for calling <c><jsf>DEFAULT</jsf>.readRecords(<jv>input</jv>)</c>. 220 * 221 * @param input The input. 222 * @return A new {@link RecordReader}. 223 * @throws IOException If a problem occurred opening the underlying input. 224 */ 225 public static RecordReader toRecords(Object input) throws IOException { 226 return DEFAULT.readRecords(input); 227 } 228 229 /** 230 * Opens a low-level {@link RecordWriter} cursor over the specified output using the {@link #DEFAULT} marshaller. 231 * 232 * <p> 233 * A shortcut for calling <c><jsf>DEFAULT</jsf>.writeRecords(<jv>output</jv>)</c>. 234 * 235 * @param output The output. 236 * @return A new {@link RecordWriter}. 237 * @throws IOException If a problem occurred opening the underlying output. 238 */ 239 public static RecordWriter ofRecords(Object output) throws IOException { 240 return DEFAULT.writeRecords(output); 241 } 242 243 /** 244 * Opens a {@link RecordReader} that yields each element of a top-level wire array using the {@link #DEFAULT} marshaller. 245 * 246 * <p> 247 * A shortcut for calling <c><jsf>DEFAULT</jsf>.readArrayRecords(<jv>input</jv>)</c>. 248 * 249 * @param input The input. 250 * @return A new {@link RecordReader}. 251 * @throws IOException If a problem occurred opening the underlying input. 252 */ 253 public static RecordReader toArrayRecords(Object input) throws IOException { 254 return DEFAULT.readArrayRecords(input); 255 } 256 257 /** 258 * Opens a {@link RecordWriter} that wraps each written value as one element of a top-level wire array using the {@link #DEFAULT} marshaller. 259 * 260 * <p> 261 * A shortcut for calling <c><jsf>DEFAULT</jsf>.writeArrayRecords(<jv>output</jv>)</c>. 262 * 263 * @param output The output. 264 * @return A new {@link RecordWriter}. 265 * @throws IOException If a problem occurred opening the underlying output. 266 */ 267 public static RecordWriter ofArrayRecords(Object output) throws IOException { 268 return DEFAULT.writeArrayRecords(output); 269 } 270 271 /** 272 * Constructor. 273 * 274 * <p> 275 * Uses {@link Json5Serializer#DEFAULT} and {@link Json5Parser#DEFAULT}. 276 */ 277 public Json5() { 278 this(Json5Serializer.DEFAULT, Json5Parser.DEFAULT); 279 } 280 281 /** 282 * Constructor. 283 * 284 * @param s 285 * The serializer to use for serializing output. 286 * <br>Must not be <jk>null</jk>. 287 * @param p 288 * The parser to use for parsing input. 289 * <br>Must not be <jk>null</jk>. 290 */ 291 public Json5(Json5Serializer s, Json5Parser p) { 292 super(s, p); 293 } 294}