Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.json.simple.Jsoner Maven / Gradle / Ivy
Go to download
Java 7+ toolkit to quickly develop RFC 4627 JSON compatible applications.
/* Copyright 2016 Clifton Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
package org.json.simple;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
/** Jsoner provides JSON utilities for escaping strings to be JSON compatible, thread safe parsing (RFC 4627) JSON
* strings, and serializing data to strings in JSON format.
* @since 2.0.0 */
public class Jsoner{
/** Flags to tweak the behavior of the primary deserialization method. */
private static enum DeserializationOptions{
/** Whether a multiple JSON values can be deserialized as a root element. */
ALLOW_CONCATENATED_JSON_VALUES,
/** Whether a JsonArray can be deserialized as a root element. */
ALLOW_JSON_ARRAYS,
/** Whether a boolean, null, Number, or String can be deserialized as a root element. */
ALLOW_JSON_DATA,
/** Whether a JsonObject can be deserialized as a root element. */
ALLOW_JSON_OBJECTS;
}
/** Flags to tweak the behavior of the primary serialization method. */
private static enum SerializationOptions{
/** Instead of aborting serialization on non-JSON values that are Enums it will continue serialization with the
* Enums' "${PACKAGE}.${DECLARING_CLASS}.${NAME}".
* @see Enum */
ALLOW_FULLY_QUALIFIED_ENUMERATIONS,
/** Instead of aborting serialization on non-JSON values it will continue serialization by serializing the
* non-JSON value directly into the now invalid JSON. Be mindful that invalid JSON will not successfully
* deserialize. */
ALLOW_INVALIDS,
/** Instead of aborting serialization on non-JSON values that implement Jsonable it will continue serialization
* by deferring serialization to the Jsonable.
* @see Jsonable */
ALLOW_JSONABLES,
/** Instead of aborting serialization on non-JSON values it will continue serialization by using reflection to
* best describe the value as a JsonObject. */
ALLOW_UNDEFINEDS;
}
/** The possible States of a JSON deserializer. */
private static enum States{
/** Post-parsing state. */
DONE,
/** Pre-parsing state. */
INITIAL,
/** Parsing error, ParsingException should be thrown. */
PARSED_ERROR,
PARSING_ARRAY,
/** Parsing a key-value pair inside of an object. */
PARSING_ENTRY,
PARSING_OBJECT;
}
private Jsoner(){
/* Keeping it classy. */
}
/** Deserializes a readable stream according to the RFC 4627 JSON specification.
* @param readableDeserializable representing content to be deserialized as JSON.
* @return either a boolean, null, Number, String, JsonObject, or JsonArray that best represents the deserializable.
* @throws DeserializationException if an unexpected token is encountered in the deserializable. To recover from a
* DeserializationException: fix the deserializable
* to no longer have an unexpected token and try again.
* @throws IOException if the underlying reader encounters an I/O error. Ensure the reader is properly instantiated,
* isn't closed, or that it is ready before trying again. */
public static Object deserialize(final Reader readableDeserializable) throws DeserializationException, IOException{
return Jsoner.deserialize(readableDeserializable, EnumSet.of(DeserializationOptions.ALLOW_JSON_ARRAYS, DeserializationOptions.ALLOW_JSON_OBJECTS, DeserializationOptions.ALLOW_JSON_DATA)).get(0);
}
/** Deserialize a stream with all deserialized JSON values are wrapped in a JsonArray.
* @param deserializable representing content to be deserialized as JSON.
* @param flags representing the allowances and restrictions on deserialization.
* @return the allowable object best represented by the deserializable.
* @throws DeserializationException if a disallowed or unexpected token is encountered in the deserializable. To
* recover from a DeserializationException: fix the
* deserializable to no longer have a disallowed or unexpected token and try again.
* @throws IOException if the underlying reader encounters an I/O error. Ensure the reader is properly instantiated,
* isn't closed, or that it is ready before trying again. */
private static JsonArray deserialize(final Reader deserializable, final Set flags) throws DeserializationException, IOException{
final Yylex lexer = new Yylex(deserializable);
Yytoken token;
States currentState;
int returnCount = 1;
final LinkedList stateStack = new LinkedList();
final LinkedList valueStack = new LinkedList();
stateStack.addLast(States.INITIAL);
//System.out.println("//////////DESERIALIZING//////////");
do{
/* Parse through the parsable string's tokens. */
currentState = Jsoner.popNextState(stateStack);
token = Jsoner.lexNextToken(lexer);
switch(currentState){
case DONE:
/* The parse has finished a JSON value. */
if(flags.contains(DeserializationOptions.ALLOW_CONCATENATED_JSON_VALUES) && !Yytoken.Types.END.equals(token.getType())){
/* Since there could be multiple JSON values treat the parse as if it is in the initial state. */
returnCount += 1;
switch(token.getType()){
case DATUM:
/* A boolean, null, Number, or String could be detected. */
if(flags.contains(DeserializationOptions.ALLOW_JSON_DATA)){
valueStack.addLast(token.getValue());
stateStack.addLast(States.DONE);
}else{
throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.DISALLOWED_TOKEN, token);
}
break;
case LEFT_BRACE:
/* An object is detected. */
if(flags.contains(DeserializationOptions.ALLOW_JSON_OBJECTS)){
valueStack.addLast(new JsonObject());
stateStack.addLast(States.PARSING_OBJECT);
}else{
throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.DISALLOWED_TOKEN, token);
}
break;
case LEFT_SQUARE:
/* An array is detected. */
if(flags.contains(DeserializationOptions.ALLOW_JSON_ARRAYS)){
valueStack.addLast(new JsonArray());
stateStack.addLast(States.PARSING_ARRAY);
}else{
throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.DISALLOWED_TOKEN, token);
}
break;
default:
/* Neither a JSON array or object was detected. */
throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.UNEXPECTED_TOKEN, token);
}
}
break;
case INITIAL:
/* The parse has just started. */
switch(token.getType()){
case DATUM:
/* A boolean, null, Number, or String could be detected. */
if(flags.contains(DeserializationOptions.ALLOW_JSON_DATA)){
valueStack.addLast(token.getValue());
stateStack.addLast(States.DONE);
}else{
throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.DISALLOWED_TOKEN, token);
}
break;
case LEFT_BRACE:
/* An object is detected. */
if(flags.contains(DeserializationOptions.ALLOW_JSON_OBJECTS)){
valueStack.addLast(new JsonObject());
stateStack.addLast(States.PARSING_OBJECT);
}else{
throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.DISALLOWED_TOKEN, token);
}
break;
case LEFT_SQUARE:
/* An array is detected. */
if(flags.contains(DeserializationOptions.ALLOW_JSON_ARRAYS)){
valueStack.addLast(new JsonArray());
stateStack.addLast(States.PARSING_ARRAY);
}else{
throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.DISALLOWED_TOKEN, token);
}
break;
default:
/* Neither a JSON array or object was detected. */
throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.UNEXPECTED_TOKEN, token);
}
break;
case PARSED_ERROR:
/* The parse could be in this state due to the state stack not having a state to pop off. */
throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.UNEXPECTED_TOKEN, token);
case PARSING_ARRAY:
switch(token.getType()){
case COMMA:
/* The parse could detect a comma while parsing an array since it separates each element. */
stateStack.addLast(currentState);
break;
case DATUM:
/* The parse found an element of the array. */
JsonArray val = (JsonArray)valueStack.getLast();
val.add(token.getValue());
stateStack.addLast(currentState);
break;
case LEFT_BRACE:
/* The parse found an object in the array. */
val = (JsonArray)valueStack.getLast();
final JsonObject object = new JsonObject();
val.add(object);
valueStack.addLast(object);
stateStack.addLast(currentState);
stateStack.addLast(States.PARSING_OBJECT);
break;
case LEFT_SQUARE:
/* The parse found another array in the array. */
val = (JsonArray)valueStack.getLast();
final JsonArray array = new JsonArray();
val.add(array);
valueStack.addLast(array);
stateStack.addLast(currentState);
stateStack.addLast(States.PARSING_ARRAY);
break;
case RIGHT_SQUARE:
/* The parse found the end of the array. */
if(valueStack.size() > returnCount){
valueStack.removeLast();
}else{
/* The parse has been fully resolved. */
stateStack.addLast(States.DONE);
}
break;
default:
/* Any other token is invalid in an array. */
throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.UNEXPECTED_TOKEN, token);
}
break;
case PARSING_OBJECT:
/* The parse has detected the start of an object. */
switch(token.getType()){
case COMMA:
/* The parse could detect a comma while parsing an object since it separates each key value
* pair. Continue parsing the object. */
stateStack.addLast(currentState);
break;
case DATUM:
/* The token ought to be a key. */
if(token.getValue() instanceof String){
/* JSON keys are always strings, strings are not always JSON keys but it is going to be
* treated as one. Continue parsing the object. */
final String key = (String)token.getValue();
valueStack.addLast(key);
stateStack.addLast(currentState);
stateStack.addLast(States.PARSING_ENTRY);
}else{
/* Abort! JSON keys are always strings and it wasn't a string. */
throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.UNEXPECTED_TOKEN, token);
}
break;
case RIGHT_BRACE:
/* The parse has found the end of the object. */
if(valueStack.size() > returnCount){
/* There are unresolved values remaining. */
valueStack.removeLast();
}else{
/* The parse has been fully resolved. */
stateStack.addLast(States.DONE);
}
break;
default:
/* The parse didn't detect the end of an object or a key. */
throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.UNEXPECTED_TOKEN, token);
}
break;
case PARSING_ENTRY:
switch(token.getType()){
/* Parsed pair keys can only happen while parsing objects. */
case COLON:
/* The parse could detect a colon while parsing a key value pair since it separates the key
* and value from each other. Continue parsing the entry. */
stateStack.addLast(currentState);
break;
case DATUM:
/* The parse has found a value for the parsed pair key. */
String key = (String)valueStack.removeLast();
JsonObject parent = (JsonObject)valueStack.getLast();
parent.put(key, token.getValue());
break;
case LEFT_BRACE:
/* The parse has found an object for the parsed pair key. */
key = (String)valueStack.removeLast();
parent = (JsonObject)valueStack.getLast();
final JsonObject object = new JsonObject();
parent.put(key, object);
valueStack.addLast(object);
stateStack.addLast(States.PARSING_OBJECT);
break;
case LEFT_SQUARE:
/* The parse has found an array for the parsed pair key. */
key = (String)valueStack.removeLast();
parent = (JsonObject)valueStack.getLast();
final JsonArray array = new JsonArray();
parent.put(key, array);
valueStack.addLast(array);
stateStack.addLast(States.PARSING_ARRAY);
break;
default:
/* The parse didn't find anything for the parsed pair key. */
throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.UNEXPECTED_TOKEN, token);
}
break;
default:
break;
}
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//System.out.println(currentState);
//System.out.println(token);
//System.out.println(valueStack);
//System.out.println(stateStack);
/* If we're not at the END and DONE then do the above again. */
}while(!(States.DONE.equals(currentState) && Yytoken.Types.END.equals(token.getType())));
//System.out.println("!!!!!!!!!!DESERIALIZED!!!!!!!!!!");
return new JsonArray(valueStack);
}
/** A convenience method that assumes a StringReader to deserialize a string.
* @param deserializable representing content to be deserialized as JSON.
* @return either a boolean, null, Number, String, JsonObject, or JsonArray that best represents the deserializable.
* @throws DeserializationException if an unexpected token is encountered in the deserializable. To recover from a
* DeserializationException: fix the deserializable
* to no longer have an unexpected token and try again.
* @see Jsoner#deserialize(Reader)
* @see StringReader */
public static Object deserialize(final String deserializable) throws DeserializationException{
Object returnable;
StringReader readableDeserializable = null;
try{
readableDeserializable = new StringReader(deserializable);
returnable = Jsoner.deserialize(readableDeserializable);
}catch(IOException | NullPointerException caught){
/* They both have the same recovery scenario.
* See StringReader.
* If deserializable is null, it should be reasonable to expect null back. */
returnable = null;
}finally{
if(readableDeserializable != null){
readableDeserializable.close();
}
}
return returnable;
}
/** A convenience method that assumes a JsonArray must be deserialized.
* @param deserializable representing content to be deserializable as a JsonArray.
* @param defaultValue representing what would be returned if deserializable isn't a JsonArray or an IOException,
* NullPointerException, or DeserializationException occurs during deserialization.
* @return a JsonArray that represents the deserializable, or the defaultValue if there isn't a JsonArray that
* represents deserializable.
* @see Jsoner#deserialize(Reader) */
public static JsonArray deserialize(final String deserializable, final JsonArray defaultValue){
StringReader readable = null;
JsonArray returnable;
try{
readable = new StringReader(deserializable);
returnable = Jsoner.deserialize(readable, EnumSet.of(DeserializationOptions.ALLOW_JSON_ARRAYS)).getCollection(0);
}catch(NullPointerException | IOException | DeserializationException caught){
/* Don't care, just return the default value. */
returnable = defaultValue;
}finally{
if(readable != null){
readable.close();
}
}
return returnable;
}
/** A convenience method that assumes a JsonObject must be deserialized.
* @param deserializable representing content to be deserializable as a JsonObject.
* @param defaultValue representing what would be returned if deserializable isn't a JsonObject or an IOException,
* NullPointerException, or DeserializationException occurs during deserialization.
* @return a JsonObject that represents the deserializable, or the defaultValue if there isn't a JsonObject that
* represents deserializable.
* @see Jsoner#deserialize(Reader) */
public static JsonObject deserialize(final String deserializable, final JsonObject defaultValue){
StringReader readable = null;
JsonObject returnable;
try{
readable = new StringReader(deserializable);
returnable = Jsoner.deserialize(readable, EnumSet.of(DeserializationOptions.ALLOW_JSON_OBJECTS)).getMap(0);
}catch(NullPointerException | IOException | DeserializationException caught){
/* Don't care, just return the default value. */
returnable = defaultValue;
}finally{
if(readable != null){
readable.close();
}
}
return returnable;
}
/** A convenience method that assumes multiple RFC 4627 JSON values (except numbers) have been concatenated together
* for deserilization which will be collectively returned in a JsonArray wrapper.
* There may be numbers included, they just must not be concatenated together as it is prone to
* NumberFormatExceptions (thus causing a DeserializationException) or the numbers no longer represent their
* respective values.
* Examples:
* "123null321" returns [123, null, 321]
* "nullnullnulltruefalse\"\"{}[]" returns [null, null, null, true, false, "", {}, []]
* "123" appended to "321" returns [123321]
* "12.3" appended to "3.21" throws DeserializationException(NumberFormatException)
* "123" appended to "-321" throws DeserializationException(NumberFormatException)
* "123e321" appended to "-1" throws DeserializationException(NumberFormatException)
* "null12.33.21null" throws DeserializationException(NumberFormatException)
* @param deserializable representing concatenated content to be deserialized as JSON in one reader. Its contents
* may
* not contain two numbers concatenated together.
* @return a JsonArray that contains each of the concatenated objects as its elements. Each concatenated element is
* either a boolean, null, Number, String, JsonArray, or JsonObject that best represents the concatenated
* content inside deserializable.
* @throws DeserializationException if an unexpected token is encountered in the deserializable. To recover from a
* DeserializationException: fix the deserializable to no longer have an unexpected token and try again.
* @throws IOException when the underlying reader encounters an I/O error. Ensure the reader is properly
* instantiated, isn't closed, or that it is ready before trying again. */
public static JsonArray deserializeMany(final Reader deserializable) throws DeserializationException, IOException{
return Jsoner.deserialize(deserializable, EnumSet.of(DeserializationOptions.ALLOW_JSON_ARRAYS, DeserializationOptions.ALLOW_JSON_OBJECTS, DeserializationOptions.ALLOW_JSON_DATA, DeserializationOptions.ALLOW_CONCATENATED_JSON_VALUES));
}
/** Escapes potentially confusing or important characters in the String provided.
* @param escapable an unescaped string.
* @return an escaped string for usage in JSON; An escaped string is one that has escaped all of the quotes ("),
* backslashes (\), return character (\r), new line character (\n), tab character (\t),
* backspace character (\b), form feed character (\f) and other control characters [u0000..u001F] or
* characters [u007F..u009F], [u2000..u20FF] with a
* backslash (\) which itself must be escaped by the backslash in a java string. */
public static String escape(final String escapable){
final StringBuilder builder = new StringBuilder();
final int characters = escapable.length();
for(int i = 0; i < characters; i++){
final char character = escapable.charAt(i);
switch(character){
case '"':
builder.append("\\\"");
break;
case '\\':
builder.append("\\\\");
break;
case '\b':
builder.append("\\b");
break;
case '\f':
builder.append("\\f");
break;
case '\n':
builder.append("\\n");
break;
case '\r':
builder.append("\\r");
break;
case '\t':
builder.append("\\t");
break;
case '/':
builder.append("\\/");
break;
default:
/* The many characters that get replaced are benign to software but could be mistaken by people
* reading it for a JSON relevant character. */
if(((character >= '\u0000') && (character <= '\u001F')) || ((character >= '\u007F') && (character <= '\u009F')) || ((character >= '\u2000') && (character <= '\u20FF'))){
final String characterHexCode = Integer.toHexString(character);
builder.append("\\u");
for(int k = 0; k < (4 - characterHexCode.length()); k++){
builder.append("0");
}
builder.append(characterHexCode.toUpperCase());
}else{
/* Character didn't need escaping. */
builder.append(character);
}
}
}
return builder.toString();
}
/** Processes the lexer's reader for the next token.
* @param lexer represents a text processor being used in the deserialization process.
* @return a token representing a meaningful element encountered by the lexer.
* @throws DeserializationException if an unexpected character is encountered while processing the text.
* @throws IOException if the underlying reader inside the lexer encounters an I/O problem, like being prematurely
* closed. */
private static Yytoken lexNextToken(final Yylex lexer) throws DeserializationException, IOException{
Yytoken returnable;
/* Parse through the next token. */
returnable = lexer.yylex();
if(returnable == null){
/* If there isn't another token, it must be the end. */
returnable = new Yytoken(Yytoken.Types.END, null);
}
return returnable;
}
/** Used for state transitions while deserializing.
* @param stateStack represents the deserialization states saved for future processing.
* @return a state for deserialization context so it knows how to consume the next token. */
private static States popNextState(final LinkedList stateStack){
if(stateStack.size() > 0){
return stateStack.removeLast();
}else{
return States.PARSED_ERROR;
}
}
/** Makes the JSON string more easily human readable.
* @param printable representing a JSON formatted string with out extraneous characters, like one returned from
* Jsoner#serialize(Object).
* @return printable except it will have '\n' then '\t' characters inserted after '[', '{', ',' and before ']' '}'
* tokens in the JSON. It will return null if printable isn't a JSON string. */
public static String prettyPrint(final String printable){
final Yylex lexer = new Yylex(new StringReader(printable));
Yytoken lexed;
final StringBuilder returnable = new StringBuilder();
final String indentation = "\t";
int level = 0;
try{
do{
lexed = Jsoner.lexNextToken(lexer);
switch(lexed.getType()){
case COMMA:
returnable.append(lexed.getValue());
returnable.append("\n");
for(int i = 0; i < level; i++){
returnable.append(indentation);
}
break;
case END:
break;
case LEFT_BRACE:
case LEFT_SQUARE:
returnable.append(lexed.getValue());
returnable.append("\n");
level++;
for(int i = 0; i < level; i++){
returnable.append(indentation);
}
break;
case RIGHT_BRACE:
case RIGHT_SQUARE:
returnable.append("\n");
level--;
for(int i = 0; i < level; i++){
returnable.append(indentation);
}
returnable.append(lexed.getValue());
break;
default:
if(lexed.getValue() instanceof String){
returnable.append("\"");
returnable.append(Jsoner.escape((String)lexed.getValue()));
returnable.append("\"");
}else{
returnable.append(lexed.getValue());
}
break;
}
//System.out.println(lexed);
}while(!lexed.getType().equals(Yytoken.Types.END));
}catch(final DeserializationException caught){
/* This is according to the method's contract. */
return null;
}catch(final IOException caught){
/* See StringReader. */
return null;
}
//System.out.println(printable);
//System.out.println(returnable);
//System.out.println(Jsoner.escape(returnable.toString()));
return returnable.toString();
}
/** A convenience method that assumes a StringWriter.
* @param jsonSerializable represents the object that should be serialized as a string in JSON format.
* @return a string, in JSON format, that represents the object provided.
* @throws IllegalArgumentException if the jsonSerializable isn't serializable in JSON.
* @see Jsoner#serialize(Object, Writer)
* @see StringWriter */
public static String serialize(final Object jsonSerializable){
final StringWriter writableDestination = new StringWriter();
try{
Jsoner.serialize(jsonSerializable, writableDestination);
}catch(final IOException caught){
/* See StringWriter. */
}
return writableDestination.toString();
}
/** Serializes values according to the RFC 4627 JSON specification. It will also trust the serialization provided by
* any Jsonables it serializes and serializes Enums that don't implement Jsonable as a string of their fully
* qualified name.
* @param jsonSerializable represents the object that should be serialized in JSON format.
* @param writableDestination represents where the resulting JSON text is written to.
* @throws IOException if the writableDestination encounters an I/O problem, like being closed while in use.
* @throws IllegalArgumentException if the jsonSerializable isn't serializable in JSON. */
public static void serialize(final Object jsonSerializable, final Writer writableDestination) throws IOException{
Jsoner.serialize(jsonSerializable, writableDestination, EnumSet.of(SerializationOptions.ALLOW_JSONABLES, SerializationOptions.ALLOW_FULLY_QUALIFIED_ENUMERATIONS));
}
/** Serialize values to JSON and write them to the provided writer based on behavior flags.
* @param jsonSerializable represents the object that should be serialized to a string in JSON format.
* @param writableDestination represents where the resulting JSON text is written to.
* @param replacement represents what is serialized instead of a non-JSON value when replacements are allowed.
* @param flags represents the allowances and restrictions on serialization.
* @throws IOException if the writableDestination encounters an I/O problem.
* @throws IllegalArgumentException if the jsonSerializable isn't serializable in JSON.
* @see SerializationOptions */
private static void serialize(final Object jsonSerializable, final Writer writableDestination, final Set flags) throws IOException{
if(jsonSerializable == null){
/* When a null is passed in the word null is supported in JSON. */
writableDestination.write("null");
}else if(((jsonSerializable instanceof Jsonable) && flags.contains(SerializationOptions.ALLOW_JSONABLES))){
/* Writes the writable as defined by the writable. */
writableDestination.write(((Jsonable)jsonSerializable).toJson());
}else if((jsonSerializable instanceof Enum) && flags.contains(SerializationOptions.ALLOW_FULLY_QUALIFIED_ENUMERATIONS)){
/* Writes the enum as a special case of string. All enums (unless they implement Jsonable) will be the
* string literal "${DECLARING_CLASS_NAME}.${ENUM_NAME}" as their value. */
@SuppressWarnings("rawtypes")
final Enum e = (Enum)jsonSerializable;
writableDestination.write('"');
writableDestination.write(e.getDeclaringClass().getName());
writableDestination.write('.');
writableDestination.write(e.name());
writableDestination.write('"');
}else if(jsonSerializable instanceof String){
/* Make sure the string is properly escaped. */
writableDestination.write('"');
writableDestination.write(Jsoner.escape((String)jsonSerializable));
writableDestination.write('"');
}else if(jsonSerializable instanceof Double){
if(((Double)jsonSerializable).isInfinite() || ((Double)jsonSerializable).isNaN()){
/* Infinite and not a number are not supported by the JSON specification, so null is used instead. */
writableDestination.write("null");
}else{
writableDestination.write(jsonSerializable.toString());
}
}else if(jsonSerializable instanceof Float){
if(((Float)jsonSerializable).isInfinite() || ((Float)jsonSerializable).isNaN()){
/* Infinite and not a number are not supported by the JSON specification, so null is used instead. */
writableDestination.write("null");
}else{
writableDestination.write(jsonSerializable.toString());
}
}else if(jsonSerializable instanceof Number){
writableDestination.write(jsonSerializable.toString());
}else if(jsonSerializable instanceof Boolean){
writableDestination.write(jsonSerializable.toString());
}else if(jsonSerializable instanceof Map){
/* Writes the map in JSON object format. */
boolean isFirstEntry = true;
@SuppressWarnings("rawtypes")
final Iterator entries = ((Map)jsonSerializable).entrySet().iterator();
writableDestination.write('{');
while(entries.hasNext()){
if(isFirstEntry){
isFirstEntry = false;
}else{
writableDestination.write(',');
}
@SuppressWarnings("rawtypes")
final Map.Entry entry = (Map.Entry)entries.next();
Jsoner.serialize(entry.getKey(), writableDestination, flags);
writableDestination.write(':');
Jsoner.serialize(entry.getValue(), writableDestination, flags);
}
writableDestination.write('}');
}else if(jsonSerializable instanceof Collection){
/* Writes the collection in JSON array format. */
boolean isFirstElement = true;
@SuppressWarnings("rawtypes")
final Iterator elements = ((Collection)jsonSerializable).iterator();
writableDestination.write('[');
while(elements.hasNext()){
if(isFirstElement){
isFirstElement = false;
}else{
writableDestination.write(',');
}
Jsoner.serialize(elements.next(), writableDestination, flags);
}
writableDestination.write(']');
}else if(jsonSerializable instanceof byte[]){
/* Writes the array in JSON array format. */
final byte[] writableArray = (byte[])jsonSerializable;
final int numberOfElements = writableArray.length;
writableDestination.write('[');
for(int i = 1; i <= numberOfElements; i++){
if(i == numberOfElements){
Jsoner.serialize(writableArray[i], writableDestination, flags);
}else{
Jsoner.serialize(writableArray[i], writableDestination, flags);
writableDestination.write(',');
}
}
writableDestination.write(']');
}else if(jsonSerializable instanceof short[]){
/* Writes the array in JSON array format. */
final short[] writableArray = (short[])jsonSerializable;
final int numberOfElements = writableArray.length;
writableDestination.write('[');
for(int i = 1; i <= numberOfElements; i++){
if(i == numberOfElements){
Jsoner.serialize(writableArray[i], writableDestination, flags);
}else{
Jsoner.serialize(writableArray[i], writableDestination, flags);
writableDestination.write(',');
}
}
writableDestination.write(']');
}else if(jsonSerializable instanceof int[]){
/* Writes the array in JSON array format. */
final int[] writableArray = (int[])jsonSerializable;
final int numberOfElements = writableArray.length;
writableDestination.write('[');
for(int i = 1; i <= numberOfElements; i++){
if(i == numberOfElements){
Jsoner.serialize(writableArray[i], writableDestination, flags);
}else{
Jsoner.serialize(writableArray[i], writableDestination, flags);
writableDestination.write(',');
}
}
writableDestination.write(']');
}else if(jsonSerializable instanceof long[]){
/* Writes the array in JSON array format. */
final long[] writableArray = (long[])jsonSerializable;
final int numberOfElements = writableArray.length;
writableDestination.write('[');
for(int i = 1; i <= numberOfElements; i++){
if(i == numberOfElements){
Jsoner.serialize(writableArray[i], writableDestination, flags);
}else{
Jsoner.serialize(writableArray[i], writableDestination, flags);
writableDestination.write(',');
}
}
writableDestination.write(']');
}else if(jsonSerializable instanceof float[]){
/* Writes the array in JSON array format. */
final float[] writableArray = (float[])jsonSerializable;
final int numberOfElements = writableArray.length;
writableDestination.write('[');
for(int i = 1; i <= numberOfElements; i++){
if(i == numberOfElements){
Jsoner.serialize(writableArray[i], writableDestination, flags);
}else{
Jsoner.serialize(writableArray[i], writableDestination, flags);
writableDestination.write(',');
}
}
writableDestination.write(']');
}else if(jsonSerializable instanceof double[]){
/* Writes the array in JSON array format. */
final double[] writableArray = (double[])jsonSerializable;
final int numberOfElements = writableArray.length;
writableDestination.write('[');
for(int i = 1; i <= numberOfElements; i++){
if(i == numberOfElements){
Jsoner.serialize(writableArray[i], writableDestination, flags);
}else{
Jsoner.serialize(writableArray[i], writableDestination, flags);
writableDestination.write(',');
}
}
writableDestination.write(']');
}else if(jsonSerializable instanceof boolean[]){
/* Writes the array in JSON array format. */
final boolean[] writableArray = (boolean[])jsonSerializable;
final int numberOfElements = writableArray.length;
writableDestination.write('[');
for(int i = 1; i <= numberOfElements; i++){
if(i == numberOfElements){
Jsoner.serialize(writableArray[i], writableDestination, flags);
}else{
Jsoner.serialize(writableArray[i], writableDestination, flags);
writableDestination.write(',');
}
}
writableDestination.write(']');
}else if(jsonSerializable instanceof char[]){
/* Writes the array in JSON array format. */
final char[] writableArray = (char[])jsonSerializable;
final int numberOfElements = writableArray.length;
writableDestination.write("[\"");
for(int i = 1; i <= numberOfElements; i++){
if(i == numberOfElements){
Jsoner.serialize(writableArray[i], writableDestination, flags);
}else{
Jsoner.serialize(writableArray[i], writableDestination, flags);
writableDestination.write("\",\"");
}
}
writableDestination.write("\"]");
}else if(jsonSerializable instanceof Object[]){
/* Writes the array in JSON array format. */
final Object[] writableArray = (Object[])jsonSerializable;
final int numberOfElements = writableArray.length;
writableDestination.write('[');
for(int i = 1; i <= numberOfElements; i++){
if(i == numberOfElements){
Jsoner.serialize(writableArray[i], writableDestination, flags);
}else{
Jsoner.serialize(writableArray[i], writableDestination, flags);
writableDestination.write(",");
}
}
writableDestination.write(']');
}else{
/* TODO a potential feature for future release since POJOs are often represented as JsonObjects. It would be
* nice to have a flag that tries to reflectively figure out what a non-Jsonable POJO's fields are and use
* their names as keys and their respective values for the keys' values in the JsonObject?
* Naturally implementing Jsonable is safer and in many ways makes this feature a convenience for not
* needing
* to implement Jsonable for very simple POJOs.
* If it fails to produce a JsonObject to serialize it should defer to replacements if allowed.
* If replacement fails it should defer to invalids if allowed.
* This feature would require another serialize method exposed to allow this serialization.
* This feature (although perhaps useful on its own) would also include a method in the JsonObject where you
* pass it a class and it would do its best to instantiate a POJO of the class using the keys in the
* JsonObject. /
* /* It cannot by any measure be safely serialized according to specification. */
if(flags.contains(SerializationOptions.ALLOW_INVALIDS)){
/* Can be helpful for debugging how it isn't valid. */
writableDestination.write(jsonSerializable.toString());
}else{
/* Notify the caller the cause of failure for the serialization. */
throw new IllegalArgumentException("Encountered a: " + jsonSerializable.getClass().getName() + " as: " + jsonSerializable.toString() + " that isn't JSON serializable.\n Try:\n 1) Implementing the Jsonable interface for the object to return valid JSON. If it already does it probably has a bug.\n 2) If you cannot edit the source of the object or couple it with this library consider wrapping it in a class that does implement the Jsonable interface.\n 3) Otherwise convert it to a boolean, null, number, JsonArray, JsonObject, or String value before serializing it.\n 4) If you feel it should have serialized you could use a more tolerant serialization for debugging purposes.");
}
}
//System.out.println(writableDestination.toString());
}
/** Serializes like the first version of this library.
* It has been adapted to use Jsonable for serializing custom objects, but otherwise works like the old JSON string
* serializer. It
* will allow non-JSON values in its output like the old one. It can be helpful for last resort log statements and
* debugging errors in self generated JSON. Anything serialized using this method isn't guaranteed to be
* deserializable.
* @param jsonSerializable represents the object that should be serialized in JSON format.
* @param writableDestination represents where the resulting JSON text is written to.
* @throws IOException if the writableDestination encounters an I/O problem, like being closed while in use. */
public static void serializeCarelessly(final Object jsonSerializable, final Writer writableDestination) throws IOException{
Jsoner.serialize(jsonSerializable, writableDestination, EnumSet.of(SerializationOptions.ALLOW_JSONABLES, SerializationOptions.ALLOW_INVALIDS));
}
/** Serializes JSON values and only JSON values according to the RFC 4627 JSON specification.
* @param jsonSerializable represents the object that should be serialized in JSON format.
* @param writableDestination represents where the resulting JSON text is written to.
* @throws IOException if the writableDestination encounters an I/O problem, like being closed while in use.
* @throws IllegalArgumentException if the jsonSerializable isn't serializable in JSON. */
public static void serializeStrictly(final Object jsonSerializable, final Writer writableDestination) throws IOException{
Jsoner.serialize(jsonSerializable, writableDestination, EnumSet.noneOf(SerializationOptions.class));
}
}