io.datatree.dom.builtin.JsonBuiltin Maven / Gradle / Ivy
/**
* This software is licensed under the Apache 2 license, quoted below.
*
* Copyright 2017 Andras Berkes [[email protected]]
*
* 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 io.datatree.dom.builtin;
import java.io.IOException;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentLinkedQueue;
import io.datatree.dom.Config;
import io.datatree.dom.Priority;
import io.datatree.dom.converters.DataConverterRegistry;
/**
* BUILT-IN JSON ADAPTER
*
* Description: Built-in JSON reader / writer.
*
* Set as default (using Java System Properties):
*
* -Ddatatree.json.reader=io.datatree.dom.builtin.JsonBuiltin
* -Ddatatree.json.writer=io.datatree.dom.builtin.JsonBuiltin
*
* Set as default (using static methods):
*
* JsonBuiltin json = new JsonBuiltin();
* TreeReaderRegistry.setReader("json", json);
* TreeWriterRegistry.setWriter("json", json);
*
* Invoke serializer and deserializer:
*
* Tree node = new Tree(inputString);
* String outputString = node.toString();
*
* @author Andras Berkes [[email protected]]
*/
@Priority(1)
public class JsonBuiltin extends AbstractTextAdapter {
// --- CONSTANTS ---
protected static final char[] APOS = "\\\"".toCharArray();
protected static final char[] CR_LF = "\r\n".toCharArray();
protected static final char[] INDENT = " ".toCharArray();
protected static final char[] NULL = "null".toCharArray();
// --- BUILDER CACHE ---
public Queue builders = new ConcurrentLinkedQueue<>();
// --- STATIC WRITER METHOD ---
protected static final JsonBuiltin instance = new JsonBuiltin();
public static final String serialize(Object value, Object meta) {
return instance.toString(value, meta, false, true);
}
// --- IMPLEMENTED WRITER METHOD ---
@Override
public String toString(Object value, Object meta, boolean pretty, boolean insertMeta) {
if (value == null) {
return "";
}
StringBuilder builder = builders.poll();
if (builder == null) {
builder = new StringBuilder(512);
} else {
builder.setLength(0);
}
toString(builder, value, insertMeta ? meta : null, pretty ? 1 : 0);
final String json = builder.toString();
if (builders.size() > Config.POOL_SIZE) {
return json;
}
builders.add(builder);
return json;
}
// --- protected UTILITIES ---
@SuppressWarnings("rawtypes")
protected static final void toString(StringBuilder builder, Object value, Object meta, int indent) {
// Null value
if (value == null) {
builder.append(NULL);
return;
}
// Numeric or boolean values
if (value instanceof Number || value instanceof Boolean) {
builder.append(value);
return;
}
// Map
if (value instanceof Map) {
Map map = (Map) value;
int max = map.size();
int pos = 0;
int newIndent = indent == 0 ? 0 : indent + 1;
builder.append('{');
if (indent != 0) {
appendIndent(builder, indent);
}
for (Object child : map.entrySet()) {
Map.Entry entry = (Map.Entry) child;
appendString(builder, entry.getKey(), false);
builder.append(':');
toString(builder, entry.getValue(), null, newIndent);
if (++pos < max || meta != null) {
builder.append(',');
if (indent != 0) {
appendIndent(builder, indent);
}
}
}
if (meta != null) {
appendString(builder, Config.META, false);
builder.append(':');
toString(builder, meta, null, newIndent);
}
if (indent != 0) {
appendIndent(builder, indent - 1);
}
builder.append('}');
return;
}
// List or Set
if (value instanceof Collection) {
builder.append('[');
if (indent != 0) {
appendIndent(builder, indent);
}
Collection array = (Collection) value;
int max = array.size();
int pos = 0;
int newIndent = indent == 0 ? 0 : indent + 1;
for (Object child : array) {
toString(builder, child, null, newIndent);
if (++pos < max) {
builder.append(',');
if (indent != 0) {
appendIndent(builder, indent);
}
}
}
if (indent != 0) {
appendIndent(builder, indent - 1);
}
builder.append(']');
return;
}
// Byte array
if (value instanceof byte[]) {
builder.append('"');
builder.append(DataConverterRegistry.convert(String.class, value));
builder.append('"');
return;
}
// Array
if (value.getClass().isArray()) {
builder.append('[');
if (indent != 0) {
appendIndent(builder, indent);
}
int max = Array.getLength(value);
int newIndent = indent == 0 ? 0 : indent + 1;
for (int i = 0; i < max; i++) {
toString(builder, Array.get(value, i), null, newIndent);
if (i < max - 1) {
builder.append(',');
if (indent != 0) {
appendIndent(builder, indent);
}
}
}
if (indent != 0) {
appendIndent(builder, indent - 1);
}
builder.append(']');
return;
}
// String and other types
appendString(builder, value, true);
}
protected static final void appendString(StringBuilder builder, Object value, boolean convert) {
String txt;
if (convert) {
txt = DataConverterRegistry.convert(String.class, value);
if (txt == null) {
builder.append(NULL);
return;
}
if (DataConverterRegistry.isUnquotedClass(value.getClass())) {
builder.append(txt);
return;
}
} else {
txt = String.valueOf(value);
}
builder.append('"');
char[] chars = txt.toCharArray();
for (char c : chars) {
if (c == '"') {
builder.append(APOS);
} else {
builder.append(c);
}
}
builder.append('"');
}
protected static final void appendIndent(StringBuilder builder, int indent) {
builder.append(CR_LF);
for (int i = 0; i < indent; i++) {
builder.append(INDENT);
}
}
// --- JSON SOURCE HOLDER ---
protected static final class Source {
private char[] chars;
private int last;
private int idx;
private char ch;
private Source(char[] chars) {
this.chars = chars;
this.last = chars.length - 1;
}
private final void set(char[] chars) {
this.chars = chars;
this.last = chars.length - 1;
this.idx = 0;
}
}
// --- SOURCE CACHE ---
public Queue
© 2015 - 2025 Weber Informatics LLC | Privacy Policy