Java tutorial
/** * JBoss, Home of Professional Open Source * Copyright Red Hat, Inc., and individual contributors. * * 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.jboss.aerogear.sync.jsonmergepatch; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.ObjectCodec; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.jboss.aerogear.sync.jsonmergepatch.JsonMergePatchEdit.Builder; import java.io.IOException; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; public final class JsonMapper { private static ObjectMapper om = createObjectMapper(); private static ObjectMapper createObjectMapper() { om = new ObjectMapper(); final SimpleModule module = new SimpleModule("JsonMergePatch", new Version(1, 0, 0, null, "aerogear", "sync")); module.addDeserializer(JsonMergePatchEdit.class, new EditDeserializer()); module.addSerializer(JsonMergePatchEdit.class, new EditSerializer()); module.addDeserializer(JsonMergePatchMessage.class, new PatchMessageDeserializer()); module.addSerializer(JsonMergePatchMessage.class, new PatchMessageSerializer()); om.registerModule(module); return om; } private JsonMapper() { } /** * Transforms from JSON to the type specified. * * @param json the json to be transformed. * @param type the Java type that the JSON should be transformed to. * @param <T> the type the class to convert to * @return T an instance of the type populated with data from the json message. */ public static <T> T fromJson(final String json, final Class<T> type) { try { return om.readValue(json, type); } catch (final Exception e) { throw new RuntimeException("error trying to parse json [" + json + ']', e); } } /** * Transforms from Java object notation to JSON. * * @param obj the Java object to transform into JSON. * @return {@code String} the json representation for the object. */ public static String toJson(final Object obj) { try { return om.writeValueAsString(obj); } catch (final Exception e) { throw new RuntimeException("error trying to parse json [" + obj + ']', e); } } public static String toString(final JsonNode jsonNode) { try { return om.writeValueAsString(jsonNode); } catch (final Exception e) { throw new RuntimeException("error trying to serialize jsonNode [" + jsonNode + ']', e); } } /** * Return a {@link JsonNode} for the passed in JSON string. * * @param json the string to be parsed. * @return JsonNode the JsonNode representing the passed-in JSON string. */ public static JsonNode asJsonNode(final String json) { try { return om.readTree(json); } catch (final IOException e) { throw new RuntimeException("error trying to parse json [" + json + ']', e); } } public static ObjectNode newObjectNode() { return om.createObjectNode(); } public static ArrayNode newArrayNode() { return om.createArrayNode(); } private static class PatchMessageDeserializer extends JsonDeserializer<JsonMergePatchMessage> { @Override public JsonMergePatchMessage deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException { final ObjectCodec oc = jp.getCodec(); final JsonNode node = oc.readTree(jp); final String documentId = node.get("id").asText(); final String clientId = node.get("clientId").asText(); final JsonNode jsonEdits = node.get("edits"); final Queue<JsonMergePatchEdit> edits = new ConcurrentLinkedQueue<JsonMergePatchEdit>(); if (jsonEdits.isArray()) { for (JsonNode edit : jsonEdits) { if (edit.isNull()) { continue; } final Builder eb = JsonMergePatchEdit.withChecksum(edit.get("checksum").asText()); eb.clientVersion(edit.get("clientVersion").asLong()); eb.serverVersion(edit.get("serverVersion").asLong()); eb.checksum(edit.get("checksum").asText()); final JsonNode diffsNode = edit.get("diffs"); if (!diffsNode.isNull()) { eb.patch(diffsNode); } edits.add(eb.build()); } } return new JsonMergePatchMessage(documentId, clientId, edits); } } private static class PatchMessageSerializer extends JsonSerializer<JsonMergePatchMessage> { @Override public void serialize(final JsonMergePatchMessage patchMessage, final JsonGenerator jgen, final SerializerProvider provider) throws IOException { jgen.writeStartObject(); jgen.writeStringField("msgType", "patch"); jgen.writeStringField("id", patchMessage.documentId()); jgen.writeStringField("clientId", patchMessage.clientId()); jgen.writeArrayFieldStart("edits"); for (JsonMergePatchEdit edit : patchMessage.edits()) { if (edit == null) { continue; } jgen.writeStartObject(); jgen.writeNumberField("clientVersion", edit.clientVersion()); jgen.writeNumberField("serverVersion", edit.serverVersion()); jgen.writeStringField("checksum", edit.checksum()); if (edit.diff() != null) { jgen.writeObjectField("diffs", edit.diff().jsonMergePatch()); } jgen.writeEndObject(); } jgen.writeEndArray(); jgen.writeEndObject(); } } private static class EditDeserializer extends JsonDeserializer<JsonMergePatchEdit> { @Override public JsonMergePatchEdit deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException { final ObjectCodec oc = jp.getCodec(); final JsonNode edit = oc.readTree(jp); final Builder eb = JsonMergePatchEdit.withChecksum(edit.get("checksum").asText()); eb.clientVersion(edit.get("clientVersion").asLong()); eb.serverVersion(edit.get("serverVersion").asLong()); final JsonNode diffsNode = edit.get("diffs"); if (!diffsNode.isNull()) { eb.patch(diffsNode); } return eb.build(); } } private static class EditSerializer extends JsonSerializer<JsonMergePatchEdit> { @Override public void serialize(final JsonMergePatchEdit edit, final JsonGenerator jgen, final SerializerProvider provider) throws IOException { jgen.writeStartObject(); jgen.writeStringField("msgType", "patch"); jgen.writeNumberField("clientVersion", edit.clientVersion()); jgen.writeNumberField("serverVersion", edit.serverVersion()); jgen.writeStringField("checksum", edit.checksum()); if (edit.diff() != null) { jgen.writeObjectField("diffs", edit.diff().jsonMergePatch()); } jgen.writeEndObject(); } } }