/*
* Copyright 2008-2016 the original author or authors.
*
* 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 com.nominanuda.zen.obj;
import static com.nominanuda.zen.common.Ex.EX;
import static com.nominanuda.zen.common.Maths.MATHS;
import static com.nominanuda.zen.common.Str.STR;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.UncheckedIOException;
import java.io.Writer;
import javax.annotation.concurrent.ThreadSafe;
//TODO test , test unicode escapeAll
@ThreadSafe
public class JsonSerializer {
public static final JsonSerializer JSON_SERIALIZER = new JsonSerializer();
public static final byte[] NULL_ON_WIRE = STR.getBytesUtf8("null");
public static final int DQUOTE_ON_WIRE = '"';
public static final byte[] TRUE_ON_WIRE = STR.getBytesUtf8("true");
public static final byte[] FALSE_ON_WIRE = STR.getBytesUtf8("false");
public static final int LCURLY_ON_WIRE = '{';
public static final int RCURLY_ON_WIRE = '}';
public static final int LSQUARE_ON_WIRE = '[';
public static final int RSQUARE_ON_WIRE = ']';
public static final int COLON_ON_WIRE = ':';
public static final int COMMA_ON_WIRE = ',';
public static final int NEWLINE_ON_WIRE = '\n';
public String toString(Object v) {
StringWriter sw = new StringWriter();
JsonOioPrinter printer = new JsonOioPrinter(sw);
serialize(v, printer);
return sw.toString();
}
public void serialize(Object v, OutputStream os) {
JsonOioPrinter printer = new JsonOioPrinter(os);
serialize(v, printer);
}
public void serialize(Object v, Writer w) {
JsonOioPrinter printer = new JsonOioPrinter(w);
serialize(v, printer);
}
public void serialize(Object v, JixHandler h) {
Any x = Any.toStruObjModel(v);
x.sendTo(h);
}
public byte[] serialize(Object v) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
JsonOioPrinter printer = new JsonOioPrinter(os);
serialize(v, printer);
return os.toByteArray();
}
@SuppressWarnings("unused")
private String serializeAndQuotePrimitive(Object val) throws UncheckedIOException {
StringWriter sw = new StringWriter();
serializeAndQuotePrimitiveTo(val, sw, false, false);
return sw.toString();
}
void serializeAndQuotePrimitiveTo(Object val, Writer w, boolean escapeSlash, boolean unicodeEscapeAll) throws UncheckedIOException {
try {
if(val == null) {
w.write("null");
} else if(val instanceof String) {
w.write("\"");
serializeNoQuoteTo((String)val, w, escapeSlash, unicodeEscapeAll);
w.write("\"");
} else if(val instanceof Number) {
w.write(MATHS.toString((Number)val));
} else {
w.write(((Boolean)val).toString());
}
} catch(IOException e) {
throw EX.uncheckedIO(e);
}
}
void serializeAndQuotePrimitiveTo(Object val, OutputStream os, boolean escapeSlash, boolean unicodeEscapeAll) throws UncheckedIOException {
try {
if(val == null) {
os.write(NULL_ON_WIRE);
} else if(val instanceof String) {
os.write(DQUOTE_ON_WIRE);
serializeNoQuoteTo((String)val, os, escapeSlash, unicodeEscapeAll);
os.write(DQUOTE_ON_WIRE);
} else if(val instanceof Number) {
serializeNoQuoteTo(MATHS.toString((Number)val), os, false, false);
} else {
os.write(((Boolean)val) ? TRUE_ON_WIRE : FALSE_ON_WIRE);
}
} catch(IOException e) {
throw EX.uncheckedIO(e);
}
}
/**
* \" \\ \/ \b \f \n \r \t \u1234 four-hex-digits
*/
private void serializeNoQuoteTo(String s, Writer writer, boolean escapeSlash, boolean unicodeEscapeAll) throws UncheckedIOException {
try {
char[] carr = s.toCharArray();
int len = carr.length;
for(int i = 0; i < len; i++) {
char c = carr[i];
switch (c) {
case '"':
writer.write("\\\"");
break;
case '/':
writer.write(escapeSlash ? "\\/" : "/");
break;
case '\\':
writer.write("\\\\");
break;
case '\b':
writer.write("\\b");
break;
case '\f':
writer.write("\\f");
break;
case '\n':
writer.write("\\n");
break;
case '\r':
writer.write("\\r");
break;
case '\t':
writer.write("\\t");
break;
default:
if(unicodeEscapeAll) {
if(c < 127) {
writer.append(c);
} else {
String hex = Integer.toHexString(c);
writer.write("\\u");
switch (hex.length()) {
case 1:
writer.write("000");
break;
case 2:
writer.write("00");
break;
case 3:
writer.write("0");
break;
default:
break;
}
writer.write(Integer.toHexString(c));
}
} else {
writer.append(c);
}
break;
}
}
} catch(IOException e) {
throw EX.uncheckedIO(e);
}
}
private void serializeNoQuoteTo(String s, OutputStream os, boolean escapeSlash, boolean unicodeEscapeAll) throws UncheckedIOException {
try {
char[] carr = s.toCharArray();
int len = carr.length;
for(int i = 0; i < len; i++) {
char c = carr[i];
switch (c) {
case '/':
if(escapeSlash) {
os.write('\\');
}
os.write('/');
break;
case '"':
os.write('\\');
os.write('"');
break;
case '\\':
os.write('\\');
os.write('\\');
break;
case '\b':
os.write('\\');
os.write('b');
break;
case '\f':
os.write('\\');
os.write('f');
break;
case '\n':
os.write('\\');
os.write('n');
break;
case '\r':
os.write('\\');
os.write('r');
break;
case '\t':
os.write('\\');
os.write('t');
break;
default:
if(unicodeEscapeAll) {
if(c < 127) {
os.write(c);
} else {
String hex = Integer.toHexString(c);
os.write('\\');
os.write('u');
switch (hex.length()) {
case 1:
os.write('0');
os.write('0');
os.write('0');
break;
case 2:
os.write('0');
os.write('0');
break;
case 3:
os.write('0');
break;
default:
break;
}
String hx = Integer.toHexString(c);
os.write(hx.charAt(0));
os.write(hx.charAt(1));
}
} else {
os.write(c);
}
break;
}
}
} catch(IOException e) {
throw EX.uncheckedIO(e);
}
}
}