package spimedb.util; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.node.ArrayNode; import com.google.common.primitives.Longs; import org.jetbrains.annotations.NotNull; import org.msgpack.jackson.dataformat.MessagePackFactory; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.OutputStream; import java.util.Base64; import java.util.Random; public class JSON { final public static ObjectMapper json = new ObjectMapper() //.disable(SerializationFeature.CLOSE_CLOSEABLE) .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) .configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false) .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) .configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true) .configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false) .configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true) .configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true) .configure(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS, true) ; final public static ObjectMapper jsonSafe = json.copy() .enable(SerializationFeature.WRAP_EXCEPTIONS) .configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true); public static final org.slf4j.Logger logger = LoggerFactory.getLogger(JSON.class); final static ThreadLocal<ObjectWriter> jsonWritersCondensed = ThreadLocal.withInitial(json::writer); final static ThreadLocal<ObjectWriter> jsonWritersPretty = ThreadLocal.withInitial(json::writerWithDefaultPrettyPrinter); static final Random uuidRandom = new Random(); public static void writeArrayValues(float[] xx, JsonGenerator jsonGenerator) throws IOException { for (float x : xx) { float y; /*if (x == Float.POSITIVE_INFINITY || x == Float.NEGATIVE_INFINITY) y = Float.NaN; //as string, "NaN" is shorter than "Infinity" else*/ y = x; jsonGenerator.writeNumber(y); } } public static void writeArrayValues(double[] xx, JsonGenerator jsonGenerator) throws IOException { for (double x : xx) { double y; /*if (x == Float.POSITIVE_INFINITY || x == Float.NEGATIVE_INFINITY) y = Float.NaN; //as string, "NaN" is shorter than "Infinity" else*/ y = x; jsonGenerator.writeNumber(y); } } public static String toJSONString(Object x) { return toJSONString(x, false); } public static String toJSONString(Object x, boolean pretty) { return new String(toJSONBytes(x, pretty)); } public static byte[] toJSONBytes(Object x) { return toJSONBytes(x, false); } public static byte[] toJSONBytes(Object x, boolean pretty) { return toJSONBytes(x, pretty ? jsonWritersPretty.get() : jsonWritersCondensed.get()); } public static byte[] toJSONBytes(Object x, ObjectWriter writer) { try { return writer.writeValueAsBytes(x); } catch (JsonProcessingException ex) { System.err.println(ex.toString()); return x.toString().getBytes(); } } public final static ObjectMapper msgPackMapper = new ObjectMapper(new MessagePackFactory()); public static byte[] toMsgPackBytes(Object x, Class c) { try { return msgPackMapper.writerFor(c).writeValueAsBytes(x); } catch (JsonProcessingException e) { logger.error("{}", e); return null; } } public static <X> X fromMsgPackBytes(byte[] msgPacked, Class<? extends X> type) { try { return msgPackMapper.reader(type).readValue(msgPacked); } catch (IOException e) { logger.error("{}", e); return null; } } public static boolean toJSON(Object x, OutputStream out) { return toJSON(x, out, (char)0); } public static boolean toJSON(Object x, OutputStream out, char suffix) { try { json.writer().writeValue(out, x); if (suffix!=0) out.write(suffix); return true; } catch (Exception ex) { logger.error("toJSON: {}", ex); return false; } } public static JsonNode fromJSON(String x) { return fromJSON(x, JsonNode.class); } public static <X> X fromJSON(String x, Class<? extends X> type) { try { return json.readValue(x, type); } catch (IOException ex) { ex.printStackTrace(); return null; } } public static <X> X fromJSON(byte[] j, Class<? extends X> type) { try { return json.readValue(j, type); } catch (IOException ex) { ex.printStackTrace(); return null; } } public static String uuid64() { //return (String)jcog.Util.uuid128(); //escaped unicode results in long JSON strings, bad //return (String)jcog.Util.uuid64(); return Base64.getEncoder().encodeToString(Longs.toByteArray(uuidRandom.nextLong())); } @NotNull public static String[] toStrings(ArrayNode a) { String[] ids = new String[a.size()]; int j = 0; for (JsonNode x : a) { ids[j++] = x.textValue(); } return ids; } } // final public static ObjectMapper jsonAnnotated = new ObjectMapper() // .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) // .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) // .configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, false) // .configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true) // .configure(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS, false) // .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); // // final public static ObjectMapper jsonFields = new ObjectMapper() // .enableDefaultTyping() // .configure(SerializationFeature.INDENT_OUTPUT, true) // .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) // .configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, false) // .configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true) // .configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true) // .configure(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS, true) // .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); // public static class BatchObjectMapper extends ObjectMapper { // // /** // * re-uses the same JSON generator for an iterator of objects to be serialized // */ // public void writeValuesNOTWORKINGYET(OutputStream out, Iterator<Object> objs, char separator) throws IOException { // // if (!objs.hasNext()) return; // // JsonGenerator gen = _jsonFactory.createGenerator(out, JsonEncoding.UTF8); // // DefaultSerializerProvider pr = _serializerProvider(getSerializationConfig()); // // // boolean hasNext; // do { // getSerializerProvider().defaultSerializeValue(objs.next(), gen); // //ser.serialize(objs.next(), gen, pr); // // hasNext = objs.hasNext(); // if (hasNext) // gen.writeRaw(separator); // // } while (hasNext); // // } // // public byte[] writeValueAsBytes(Object value, ByteArrayBuilder bb) // throws JsonProcessingException // { // try { // _configAndWriteValue(_jsonFactory.createGenerator(bb, JsonEncoding.UTF8), value); // } catch (JsonProcessingException e) { // to support [JACKSON-758] // throw e; // } catch (IOException e) { // shouldn't really happen, but is declared as possibility so: // throw JsonMappingException.fromUnexpectedIOE(e); // } // byte[] result = bb.toByteArray(); // bb.reset(); // return result; // } // // public void writeValues(OutputStream out, Iterator<Object> objs, char separator) throws IOException { // // if (!objs.hasNext()) return; // // ByteArrayBuilder bb = new ByteArrayBuilder(_jsonFactory._getBufferRecycler()); // // boolean hasNext; // do { // out.write(writeValueAsBytes(objs.next(), bb)); // // hasNext = objs.hasNext(); // if (hasNext) // out.write(separator); // // } while (hasNext); // // bb.release(); // // // } // // // public void writeArrayValues(OutputStream out, Iterator objs, char separator) throws IOException { // out.write('['); // writeValues(out, objs, separator); // out.write(']'); // } // }