001/* 002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nicolas Chapurlat <nchapurlat@nuxeo.com> 018 */ 019 020package org.nuxeo.ecm.core.io.marshallers.json; 021 022import static javax.ws.rs.core.MediaType.APPLICATION_JSON; 023import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; 024 025import java.io.IOException; 026import java.io.InputStream; 027import java.lang.reflect.Type; 028 029import javax.inject.Inject; 030import javax.ws.rs.core.MediaType; 031 032import org.codehaus.jackson.JsonNode; 033import org.codehaus.jackson.JsonParseException; 034import org.codehaus.jackson.JsonParser; 035import org.codehaus.jackson.JsonProcessingException; 036import org.nuxeo.ecm.core.io.registry.MarshallerRegistry; 037import org.nuxeo.ecm.core.io.registry.Reader; 038import org.nuxeo.ecm.core.io.registry.context.RenderingContext; 039import org.nuxeo.ecm.core.io.registry.reflect.Supports; 040 041/** 042 * Base class for Json {@link Reader}. 043 * <p> 044 * This class provides an easy way to create java object from json and also provides the current context: 045 * {@link AbstractJsonReader#ctx}. It provides you a {@link JsonNode} to manage the unmarshalling. 046 * </p> 047 * <p> 048 * The use of this class optimize the JsonFactory usage especially when aggregating unmarshallers. 049 * </p> 050 * 051 * @param <EntityType> The expected Java type. 052 * @since 7.2 053 */ 054@Supports(APPLICATION_JSON) 055public abstract class AbstractJsonReader<EntityType> implements Reader<EntityType> { 056 057 /** 058 * The current {@link RenderingContext}. 059 */ 060 @Inject 061 protected RenderingContext ctx; 062 063 /** 064 * The marshaller registry. You may use it to use other marshallers. 065 */ 066 @Inject 067 protected MarshallerRegistry registry; 068 069 @Override 070 public boolean accept(Class<?> clazz, Type genericType, MediaType mediatype) { 071 return true; 072 } 073 074 @Override 075 public EntityType read(Class<?> clazz, Type genericType, MediaType mediaType, InputStream in) throws IOException { 076 JsonNode jn = getNode(in, true); 077 return read(jn); 078 } 079 080 /** 081 * Provide a {@link JsonNode}, try to get it from the context. 082 * 083 * @param in The current {@link InputStream}. 084 * @param getCurrentIfAvailable If true, try to get it from the context (if another marshaller already create it and 085 * call this marshaller). 086 * @return A valid {@link JsonNode}. 087 * @since 7.2 088 */ 089 protected JsonNode getNode(InputStream in, boolean getCurrentIfAvailable) throws IOException, JsonParseException, 090 JsonProcessingException { 091 if (getCurrentIfAvailable) { 092 if (in instanceof InputStreamWithJsonNode) { 093 return ((InputStreamWithJsonNode) in).getJsonNode(); 094 } 095 } 096 JsonParser jp = JsonFactoryProvider.get().createJsonParser(in); 097 JsonNode jn = jp.readValueAsTree(); 098 return jn; 099 } 100 101 /** 102 * Implement this method, read the entity data in the provided {@link JsonNode} and return corresponding java 103 * object. 104 * 105 * @param jn A ready to use {@link JsonNode}. 106 * @return The unmarshalled entity. 107 * @since 7.2 108 */ 109 public abstract EntityType read(JsonNode jn) throws IOException; 110 111 /** 112 * Use this method to delegate the unmarshalling of a part or your Json to the {@link MarshallerRegistry}. This will 113 * work only if a Json {@link Reader} is registered for the provided clazz and if the node format is the same as the 114 * one expected by the marshaller. 115 * 116 * @param clazz The expected Java class. 117 * @param genericType The generic type of the expected object: usefull if it's a generic List for example (use 118 * TypeUtils to create the parametrize type). 119 * @param jn The {@link JsonNode} to unmarshall. 120 * @return An object implementing the expected clazz. 121 * @since 7.2 122 */ 123 @SuppressWarnings("unchecked") 124 protected <T> T readEntity(Class<?> clazz, Type genericType, JsonNode jn) throws IOException { 125 Type effectiveGenericType = genericType != null ? genericType : clazz; 126 Reader<T> reader = (Reader<T>) registry.getReader(ctx, clazz, effectiveGenericType, APPLICATION_JSON_TYPE); 127 return reader.read(clazz, effectiveGenericType, APPLICATION_JSON_TYPE, new InputStreamWithJsonNode(jn)); 128 } 129 130 /** 131 * Try to get a string property of the given {@link JsonNode}. Return null if the node is null. 132 * 133 * @param jn The {@link JsonNode} to parse. 134 * @param elName The property name. 135 * @return The property text if it exists and it's a text, null otherwise. 136 * @since 7.2 137 */ 138 protected String getStringField(JsonNode jn, String elName) { 139 JsonNode elNode = jn.get(elName); 140 if (elNode != null && !elNode.isNull() && elNode.isTextual()) { 141 return elNode.getTextValue(); 142 } else { 143 return null; 144 } 145 } 146 147}