All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.eatthepath.json.JsonSerializer Maven / Gradle / Ivy

package com.eatthepath.json;

import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;

/**
 * 

A JSON serializer is responsible for producing complete and legal JSON texts (objects or arrays). It can serialize * a {@link Map} as a JSON object or any {@link Collection} or array as a JSON list. The keys of a {@code Map} are * always serialized as strings. Values within a {@code Map}, {@code Collection}, or array are converted to JSON values * as follows:

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Mapping of Java types to JSON types
Object typeJSON representation
{@code null}{@code null}
{@link Map}{@code object}
{@link Collection}{@code list}
{@code byte[]}{@code list}
{@code short[]}{@code list}
{@code int[]}{@code list}
{@code long[]}{@code list}
{@code float[]}{@code list}
{@code double[]}{@code list}
{@code boolean[]}{@code list}
{@code char[]}{@code list}
{@link Object}{@code []}{@code list}
{@link Number}{@code number} (or {@code null})
{@link Boolean}{@code true} or {@code false} (or {@code null})
all othersthe result of {@code obj.toString()}
* * @author Jon Chambers * * @see ECMA-404 The JSON Data * Interchange Standard */ public class JsonSerializer { /** * Don't allow direct instantiation. * * @throws InstantiationException in all cases */ JsonSerializer() throws InstantiationException { throw new InstantiationException("JsonSerializer must not be instantiated directly."); } /** * Writes the given {@link Map} as a JSON object to the given {@link Appendable}. Note that the keys of the given * map will be represented as {@code Strings} (via their {@link Object#toString() toString} method) regardless of * their actual type. * * @param map the map to write as a JSON text * @param out the {@code Appendable} to which to write the JSON text * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ public static void writeJsonText(final Map map, final Appendable out) throws IOException { if (map == null) { out.append("null"); } else if (map.isEmpty()) { out.append("{}"); } else { out.append('{'); boolean valuesWritten = false; for (final Map.Entry entry : map.entrySet()) { if (entry.getKey() == null) { throw new NullPointerException("JSON object keys must not be null."); } if (valuesWritten) { out.append(','); } writeJsonValue(entry.getKey().toString(), out); out.append(':'); writeJsonValue(entry.getValue(), out); valuesWritten = true; } out.append('}'); } } /** * Returns the given {@link Map} as a JSON string. Note that the keys of the given map will be represented as * {@code Strings} (via their {@link Object#toString() toString} method) regardless of their actual type. * * @param map the map to return as a JSON text * * @return a JSON string representing the given map */ public static String writeJsonTextAsString(final Map map) { final StringBuilder stringBuilder = new StringBuilder(); try { writeJsonText(map, stringBuilder); return stringBuilder.toString(); } catch (final IOException e) { // This should never happen for a StringBuilder throw new RuntimeException(e); } } /** * Writes the given {@link Collection} as a JSON list to the given {@link Appendable}. * * @param collection the collection to write as a JSON value * @param out the {@code Appendable} to which to write the JSON text * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ public static void writeJsonText(final Collection collection, final Appendable out) throws IOException { if (collection == null) { out.append("null"); } else if (collection.isEmpty()) { out.append("[]"); } else { out.append('['); boolean valuesWritten = false; for (final Object o : collection) { if (valuesWritten) { out.append(','); } writeJsonValue(o, out); valuesWritten = true; } out.append(']'); } } /** * Returns the given {@link Collection} as a JSON string. * * @param collection the collection to return as a JSON text * * @return a JSON string representing the given collection */ public static String writeJsonTextAsString(final Collection collection) { final StringBuilder stringBuilder = new StringBuilder(); try { writeJsonText(collection, stringBuilder); return stringBuilder.toString(); } catch (final IOException e) { // This should never happen for a StringBuilder throw new RuntimeException(e); } } /** * Writes the given array of {@code bytes} as a JSON list to the given {@link Appendable}. * * @param array the array to write as a JSON value * @param out the {@code Appendable} to which to write the JSON text * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ public static void writeJsonText(final byte[] array, final Appendable out) throws IOException { if (array == null) { out.append("null"); } else if (array.length == 0) { out.append("[]"); } else { out.append('['); out.append(String.valueOf(array[0])); for (int i = 1; i < array.length; i++) { out.append(','); out.append(String.valueOf(array[i])); } out.append(']'); } } /** * Returns the given array of {@code bytes} as a JSON string. * * @param array the array to return as a JSON text * * @return a JSON string representing the given array */ public static String writeJsonTextAsString(final byte[] array) { final StringBuilder stringBuilder = new StringBuilder(); try { writeJsonText(array, stringBuilder); return stringBuilder.toString(); } catch (final IOException e) { // This should never happen for a StringBuilder throw new RuntimeException(e); } } /** * Writes the given array of {@code shorts} as a JSON list to the given {@link Appendable}. * * @param array the array to write as a JSON value * @param out the {@code Appendable} to which to write the JSON text * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ public static void writeJsonText(final short[] array, final Appendable out) throws IOException { if (array == null) { out.append("null"); } else if (array.length == 0) { out.append("[]"); } else { out.append('['); out.append(String.valueOf(array[0])); for (int i = 1; i < array.length; i++) { out.append(','); out.append(String.valueOf(array[i])); } out.append(']'); } } /** * Returns the given array of {@code shorts} as a JSON string. * * @param array the array to return as a JSON text * * @return a JSON string representing the given array */ public static String writeJsonTextAsString(final short[] array) { final StringBuilder stringBuilder = new StringBuilder(); try { writeJsonText(array, stringBuilder); return stringBuilder.toString(); } catch (final IOException e) { // This should never happen for a StringBuilder throw new RuntimeException(e); } } /** * Writes the given array of {@code ints} as a JSON list to the given {@link Appendable}. * * @param array the array to write as a JSON value * @param out the {@code Appendable} to which to write the JSON text * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ public static void writeJsonText(final int[] array, final Appendable out) throws IOException { if (array == null) { out.append("null"); } else if (array.length == 0) { out.append("[]"); } else { out.append('['); out.append(String.valueOf(array[0])); for (int i = 1; i < array.length; i++) { out.append(','); out.append(String.valueOf(array[i])); } out.append(']'); } } /** * Returns the given array of {@code ints} as a JSON string. * * @param array the array to return as a JSON text * * @return a JSON string representing the given array */ public static String writeJsonTextAsString(final int[] array) { final StringBuilder stringBuilder = new StringBuilder(); try { writeJsonText(array, stringBuilder); return stringBuilder.toString(); } catch (final IOException e) { // This should never happen for a StringBuilder throw new RuntimeException(e); } } /** * Writes the given array of {@code longs} as a JSON list to the given {@link Appendable}. * * @param array the array to write as a JSON value * @param out the {@code Appendable} to which to write the JSON text * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ public static void writeJsonText(final long[] array, final Appendable out) throws IOException { if (array == null) { out.append("null"); } else if (array.length == 0) { out.append("[]"); } else { out.append('['); out.append(String.valueOf(array[0])); for (int i = 1; i < array.length; i++) { out.append(','); out.append(String.valueOf(array[i])); } out.append(']'); } } /** * Returns the given array of {@code longs} as a JSON string. * * @param array the array to return as a JSON text * * @return a JSON string representing the given array */ public static String writeJsonTextAsString(final long[] array) { final StringBuilder stringBuilder = new StringBuilder(); try { writeJsonText(array, stringBuilder); return stringBuilder.toString(); } catch (final IOException e) { // This should never happen for a StringBuilder throw new RuntimeException(e); } } /** * Writes the given array of {@code floats} as a JSON list to the given {@link Appendable}. * * @param array the array to write as a JSON value * @param out the {@code Appendable} to which to write the JSON text * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ public static void writeJsonText(final float[] array, final Appendable out) throws IOException { if (array == null) { out.append("null"); } else if (array.length == 0) { out.append("[]"); } else { out.append('['); out.append(String.valueOf(array[0])); for (int i = 1; i < array.length; i++) { out.append(','); out.append(String.valueOf(array[i])); } out.append(']'); } } /** * Returns the given array of {@code floats} as a JSON string. * * @param array the array to return as a JSON text * * @return a JSON string representing the given array */ public static String writeJsonTextAsString(final float[] array) { final StringBuilder stringBuilder = new StringBuilder(); try { writeJsonText(array, stringBuilder); return stringBuilder.toString(); } catch (final IOException e) { // This should never happen for a StringBuilder throw new RuntimeException(e); } } /** * Writes the given array of {@code doubles} as a JSON list to the given {@link Appendable}. * * @param array the array to write as a JSON value * @param out the {@code Appendable} to which to write the JSON text * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ public static void writeJsonText(final double[] array, final Appendable out) throws IOException { if (array == null) { out.append("null"); } else if (array.length == 0) { out.append("[]"); } else { out.append('['); out.append(String.valueOf(array[0])); for (int i = 1; i < array.length; i++) { out.append(','); out.append(String.valueOf(array[i])); } out.append(']'); } } /** * Returns the given array of {@code doubles} as a JSON string. * * @param array the array to return as a JSON text * * @return a JSON string representing the given array */ public static String writeJsonTextAsString(final double[] array) { final StringBuilder stringBuilder = new StringBuilder(); try { writeJsonText(array, stringBuilder); return stringBuilder.toString(); } catch (final IOException e) { // This should never happen for a StringBuilder throw new RuntimeException(e); } } /** * Writes the given array of {@code booleans} as a JSON list to the given {@link Appendable}. * * @param array the array to write as a JSON value * @param out the {@code Appendable} to which to write the JSON text * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ public static void writeJsonText(final boolean[] array, final Appendable out) throws IOException { if (array == null) { out.append("null"); } else if (array.length == 0) { out.append("[]"); } else { out.append('['); out.append(String.valueOf(array[0])); for (int i = 1; i < array.length; i++) { out.append(','); out.append(String.valueOf(array[i])); } out.append(']'); } } /** * Returns the given array of {@code booleans} as a JSON string. * * @param array the array to return as a JSON text * * @return a JSON string representing the given array */ public static String writeJsonTextAsString(final boolean[] array) { final StringBuilder stringBuilder = new StringBuilder(); try { writeJsonText(array, stringBuilder); return stringBuilder.toString(); } catch (final IOException e) { // This should never happen for a StringBuilder throw new RuntimeException(e); } } /** * Writes the given array of characters as a JSON list to the given {@link Appendable}. * * @param array the array to write as a JSON value * @param out the {@code Appendable} to which to write the JSON text * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ public static void writeJsonText(final char[] array, final Appendable out) throws IOException { if (array == null) { out.append("null"); } else if (array.length == 0) { out.append("[]"); } else { out.append('['); writeJsonValue(array[0], out); for (int i = 1; i < array.length; i++) { out.append(','); writeJsonValue(array[i], out); } out.append(']'); } } /** * Returns the given array of {@code chars} as a JSON string. * * @param array the array to return as a JSON text * * @return a JSON string representing the given array */ public static String writeJsonTextAsString(final char[] array) { final StringBuilder stringBuilder = new StringBuilder(); try { writeJsonText(array, stringBuilder); return stringBuilder.toString(); } catch (final IOException e) { // This should never happen for a StringBuilder throw new RuntimeException(e); } } /** * Writes the given array of {@link Object Objects} as a JSON list to the given {@link Appendable}. * * @param array the array to write as a JSON value * @param out the {@code Appendable} to which to write the JSON text * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ public static void writeJsonText(final Object[] array, final Appendable out) throws IOException { if (array == null) { out.append("null"); } else if (array.length == 0) { out.append("[]"); } else { out.append('['); writeJsonValue(array[0], out); for (int i = 1; i < array.length; i++) { out.append(','); writeJsonValue(array[i], out); } out.append(']'); } } /** * Returns the given array of {@code Objects} as a JSON string. * * @param array the array to return as a JSON text * * @return a JSON string representing the given array */ public static String writeJsonTextAsString(final Object[] array) { final StringBuilder stringBuilder = new StringBuilder(); try { writeJsonText(array, stringBuilder); return stringBuilder.toString(); } catch (final IOException e) { // This should never happen for a StringBuilder throw new RuntimeException(e); } } /** * Writes the given object as a JSON value. * * @param o the object to write as a JSON value * @param out the {@code Appendable} to which to write the JSON value * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ static void writeJsonValue(final Object o, final Appendable out) throws IOException { if (o == null) { out.append("null"); } else if (o instanceof Optional) { final Optional optional = (Optional) o; if (optional.isPresent()) { writeJsonValue(optional.get(), out); } else { writeJsonValue((Object) null, out); } } else if (o instanceof Map) { writeJsonText((Map) o, out); } else if (o instanceof Collection) { writeJsonText((Collection) o, out); } else if (o instanceof byte[]) { writeJsonText((byte[]) o, out); } else if (o instanceof short[]) { writeJsonText((short[]) o, out); } else if (o instanceof int[]) { writeJsonText((int[]) o, out); } else if (o instanceof long[]) { writeJsonText((long[]) o, out); } else if (o instanceof float[]) { writeJsonText((float[]) o, out); } else if (o instanceof double[]) { writeJsonText((double[]) o, out); } else if (o instanceof boolean[]) { writeJsonText((boolean[]) o, out); } else if (o instanceof char[]) { writeJsonText((char[]) o, out); } else if (o instanceof Object[]) { writeJsonText((Object[]) o, out); } else if (o instanceof Number) { writeJsonValue((Number) o, out); } else if (o instanceof Boolean) { writeJsonValue((Boolean) o, out); } else { writeJsonValue(o.toString(), out); } } /** * Writes the given {@link String} as a JSON string. * * @param string the string to write as a JSON value * @param out the {@code Appendable} to which to write the JSON value * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ static void writeJsonValue(final String string, final Appendable out) throws IOException { if (string == null) { out.append("null"); } else if ("".equals(string)) { out.append("\"\""); } else { out.append('\"'); int start = 0; // The strategy here is to make as few writes as possible by looking for substrings of characters that don't // need to be escaped. Whenever we hit a character that needs to be escaped, we write all of the // previously-unwritten characters up to that point, then add the escaped version of that character. for (int i = 0; i < string.length(); i++) { final char c = string.charAt(i); if (c == '"' || c == '\\' || c == '/' || c == '\b' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || Character.isISOControl(c)) { out.append(string, start, i); appendEscapedCharacter(c, out); start = i + 1; } } if (start == 0) { // We hit the end of the string without appending anything out.append(string); } else if (start < string.length()) { out.append(string, start, string.length()); } out.append('\"'); } } /** * Writes the given {@code char} as a JSON string. * * @param c the character to write as a JSON value * @param out the {@code Appendable} to which to write the JSON value * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ static void writeJsonValue(final char c, final Appendable out) throws IOException { out.append('"'); appendEscapedCharacter(c, out); out.append('"'); } /** * Appends the given {@code char} to the given {@link Appendable}, escaping it if necessary. * * @param c the character to append * @param out the {@code Appendable} to which to write the given character * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ private static void appendEscapedCharacter(final char c, final Appendable out) throws IOException { switch (c) { case '"': out.append("\\\""); break; case '\\': out.append("\\\\"); break; case '/': out.append("\\/"); break; case '\b': out.append("\\b"); break; case '\f': out.append("\\f"); break; case '\n': out.append("\\n"); break; case '\r': out.append("\\r"); break; case '\t': out.append("\\t"); break; default: if (Character.isISOControl(c)) { out.append("\\u"); out.append(Integer.toHexString(0x10000 | c), 1, 5); } else { out.append(c); } break; } } /** * Writes the given {@link Number} as a JSON number. * * @param number the number to write as a JSON value * @param out the {@code Appendable} to which to write the JSON value * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ static void writeJsonValue(final Number number, final Appendable out) throws IOException { if (number == null) { out.append("null"); } else if (number instanceof Float) { // Floats can have NaN or infinite values; pass them off to a dedicated handler writeJsonValue(number.floatValue(), out); } else if (number instanceof Double) { // Doubles can have NaN or infinite values; pass them off to a dedicated handler writeJsonValue(number.doubleValue(), out); } else { out.append(number.toString()); } } /** * Writes the given {@code byte} as a JSON number. * * @param b the byte to write as a JSON value * @param out the {@code Appendable} to which to write the JSON value * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ static void writeJsonValue(final byte b, final Appendable out) throws IOException { out.append(Byte.toString(b)); } /** * Writes the given {@code short} as a JSON number. * * @param s the short to write as a JSON value * @param out the {@code Appendable} to which to write the JSON value * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ static void writeJsonValue(final short s, final Appendable out) throws IOException { out.append(Short.toString(s)); } /** * Writes the given {@code int} as a JSON number. * * @param i the integer to write as a JSON value * @param out the {@code Appendable} to which to write the JSON value * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ static void writeJsonValue(final int i, final Appendable out) throws IOException { out.append(Integer.toString(i)); } /** * Writes the given {@code long} as a JSON number. * * @param l the long to write as a JSON value * @param out the {@code Appendable} to which to write the JSON value * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ static void writeJsonValue(final long l, final Appendable out) throws IOException { out.append(Long.toString(l)); } /** * Writes the given {@code float} as a JSON number. Note that {@code NaN} values and non-finite numbers cannot be * written as JSON values. * * @param f the float to write as a JSON value * @param out the {@code Appendable} to which to write the JSON value * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} * @throws IllegalArgumentException if the given float was not a number or was not finite */ static void writeJsonValue(final float f, final Appendable out) throws IOException { if (Float.isInfinite(f)) { throw new IllegalArgumentException("Cannot write non-finite numbers as JSON values."); } if (Float.isNaN(f)) { throw new IllegalArgumentException("Cannot write NaN as a JSON value."); } out.append(Float.toString(f)); } /** * Writes the given {@code double} as a JSON number. Note that {@code NaN} values and non-finite numbers cannot be * written as JSON values. * * @param d the double to write as a JSON value * @param out the {@code Appendable} to which to write the JSON value * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} * @throws IllegalArgumentException if the given double was not a number or was not finite */ static void writeJsonValue(final double d, final Appendable out) throws IOException { if (Double.isInfinite(d)) { throw new IllegalArgumentException("Cannot write non-finite numbers as JSON values."); } if (Double.isNaN(d)) { throw new IllegalArgumentException("Cannot write NaN as a JSON value."); } out.append(Double.toString(d)); } /** * Writes the given {@link Boolean} as a JSON value. * * @param b the boolean to write as a JSON value * @param out the {@code Appendable} to which to write the JSON value * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ static void writeJsonValue(final Boolean b, final Appendable out) throws IOException { if (b == null) { out.append("null"); } else { writeJsonValue(b.booleanValue(), out); } } /** * Writes the given {@code boolean} as a JSON value. * * @param b the boolean to write as a JSON value * @param out the {@code Appendable} to which to write the JSON value * * @throws IOException in the event of an I/O error when writing to the given {@code Appendable} */ static void writeJsonValue(final boolean b, final Appendable out) throws IOException { out.append(b ? "true" : "false"); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy