/* * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others. * * 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. * * Contributors: * Nicolas Chapurlat <nchapurlat@nuxeo.com> */ package org.nuxeo.ecm.core.io.marshallers.json; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; import javax.inject.Inject; import javax.ws.rs.core.MediaType; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonProcessingException; import org.nuxeo.ecm.core.io.registry.MarshallerRegistry; import org.nuxeo.ecm.core.io.registry.Reader; import org.nuxeo.ecm.core.io.registry.context.RenderingContext; import org.nuxeo.ecm.core.io.registry.reflect.Supports; /** * Base class for Json {@link Reader}. * <p> * This class provides an easy way to create java object from json and also provides the current context: * {@link AbstractJsonReader#ctx}. It provides you a {@link JsonNode} to manage the unmarshalling. * </p> * <p> * The use of this class optimize the JsonFactory usage especially when aggregating unmarshallers. * </p> * * @param <EntityType> The expected Java type. * @since 7.2 */ @Supports(APPLICATION_JSON) public abstract class AbstractJsonReader<EntityType> implements Reader<EntityType> { /** * The current {@link RenderingContext}. */ @Inject protected RenderingContext ctx; /** * The marshaller registry. You may use it to use other marshallers. */ @Inject protected MarshallerRegistry registry; @Override public boolean accept(Class<?> clazz, Type genericType, MediaType mediatype) { return true; } @Override public EntityType read(Class<?> clazz, Type genericType, MediaType mediaType, InputStream in) throws IOException { JsonNode jn = getNode(in, true); return read(jn); } /** * Provide a {@link JsonNode}, try to get it from the context. * * @param in The current {@link InputStream}. * @param getCurrentIfAvailable If true, try to get it from the context (if another marshaller already create it and * call this marshaller). * @return A valid {@link JsonNode}. * @since 7.2 */ protected JsonNode getNode(InputStream in, boolean getCurrentIfAvailable) throws IOException, JsonParseException, JsonProcessingException { if (getCurrentIfAvailable) { if (in instanceof InputStreamWithJsonNode) { return ((InputStreamWithJsonNode) in).getJsonNode(); } } JsonParser jp = JsonFactoryProvider.get().createJsonParser(in); JsonNode jn = jp.readValueAsTree(); return jn; } /** * Implement this method, read the entity data in the provided {@link JsonNode} and return corresponding java * object. * * @param jn A ready to use {@link JsonNode}. * @return The unmarshalled entity. * @since 7.2 */ public abstract EntityType read(JsonNode jn) throws IOException; /** * Use this method to delegate the unmarshalling of a part or your Json to the {@link MarshallerRegistry}. This will * work only if a Json {@link Reader} is registered for the provided clazz and if the node format is the same as the * one expected by the marshaller. * * @param clazz The expected Java class. * @param genericType The generic type of the expected object: usefull if it's a generic List for example (use * TypeUtils to create the parametrize type). * @param jn The {@link JsonNode} to unmarshall. * @return An object implementing the expected clazz. * @since 7.2 */ @SuppressWarnings("unchecked") protected <T> T readEntity(Class<?> clazz, Type genericType, JsonNode jn) throws IOException { Type effectiveGenericType = genericType != null ? genericType : clazz; Reader<T> reader = (Reader<T>) registry.getReader(ctx, clazz, effectiveGenericType, APPLICATION_JSON_TYPE); return reader.read(clazz, effectiveGenericType, APPLICATION_JSON_TYPE, new InputStreamWithJsonNode(jn)); } /** * Try to get a string property of the given {@link JsonNode}. Return null if the node is null. * * @param jn The {@link JsonNode} to parse. * @param elName The property name. * @return The property text if it exists and it's a text, null otherwise. * @since 7.2 */ protected String getStringField(JsonNode jn, String elName) { JsonNode elNode = jn.get(elName); if (elNode != null && !elNode.isNull() && elNode.isTextual()) { return elNode.getTextValue(); } else { return null; } } }