| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.tuscany.sca.databinding.json.jackson; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.Reader; |
| import java.io.StringWriter; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.Stack; |
| |
| import javax.xml.bind.annotation.adapters.XmlAdapter; |
| import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; |
| import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters; |
| |
| import org.apache.tuscany.sca.databinding.TransformationContext; |
| import org.codehaus.jackson.JsonEncoding; |
| import org.codehaus.jackson.JsonFactory; |
| import org.codehaus.jackson.JsonGenerator; |
| import org.codehaus.jackson.JsonNode; |
| import org.codehaus.jackson.JsonParser; |
| import org.codehaus.jackson.map.AnnotationIntrospector; |
| import org.codehaus.jackson.map.DeserializationConfig; |
| import org.codehaus.jackson.map.MappingJsonFactory; |
| import org.codehaus.jackson.map.ObjectMapper; |
| import org.codehaus.jackson.map.SerializerProvider; |
| import org.codehaus.jackson.map.annotate.JsonSerialize; |
| import org.codehaus.jackson.map.deser.BeanDeserializerFactory; |
| import org.codehaus.jackson.map.deser.StdDeserializerProvider; |
| import org.codehaus.jackson.map.introspect.AnnotatedClass; |
| import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector; |
| import org.codehaus.jackson.map.module.SimpleDeserializers; |
| import org.codehaus.jackson.map.ser.BeanPropertyWriter; |
| import org.codehaus.jackson.map.ser.CustomSerializerFactory; |
| import org.codehaus.jackson.map.ser.FilterProvider; |
| import org.codehaus.jackson.map.ser.impl.SimpleBeanPropertyFilter; |
| import org.codehaus.jackson.map.ser.impl.SimpleFilterProvider; |
| import org.codehaus.jackson.map.util.StdDateFormat; |
| import org.codehaus.jackson.xc.JaxbAnnotationIntrospector; |
| import org.codehaus.jackson.xc.XmlAdapterJsonDeserializer; |
| import org.codehaus.jackson.xc.XmlAdapterJsonSerializer; |
| import org.json.JSONObject; |
| |
| import com.fasterxml.jackson.module.jsonorg.JsonOrgModule; |
| |
| /** |
| * Helper class for Jackson |
| */ |
| public class JacksonHelper { |
| public static final String TUSCANY_FILTER = "tuscanyFilter"; |
| public static final String EXCLUDED_FIELDS = "excludedFields"; |
| public static final String INCLUDED_FIELDS = "includedFields"; |
| private final static SimpleBeanPropertyFilter DEFAULT_FILTER = SimpleBeanPropertyFilter.serializeAllExcept(); |
| |
| /** |
| * The default instance of Jackson ObjectMapper |
| */ |
| public final static ObjectMapper MAPPER = createMapper(); |
| private final static JsonFactory FACTORY = new MappingJsonFactory(createMapper()); |
| |
| public static ObjectMapper createMapper() { |
| return createObjectMapper(null); |
| } |
| |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| public static ObjectMapper createObjectMapper(Class<?> cls) { |
| ObjectMapper mapper = null; |
| if (cls != null) { |
| // Workaround for http://jira.codehaus.org/browse/JACKSON-413 |
| Package pkg = cls.getPackage(); |
| if (pkg != null) { |
| XmlJavaTypeAdapters adapters = pkg.getAnnotation(XmlJavaTypeAdapters.class); |
| if (adapters != null) { |
| CustomSerializerFactory serializerFactory = new CustomSerializerFactory(); |
| BeanDeserializerFactory deserializerFactory = new BeanDeserializerFactory(null); |
| for (XmlJavaTypeAdapter a : adapters.value()) { |
| XmlAdapter xmlAdapter = null; |
| try { |
| xmlAdapter = a.value().newInstance(); |
| } catch (Throwable e) { |
| // Ignore |
| } |
| if (xmlAdapter != null) { |
| XmlAdapterJsonDeserializer deserializer = new XmlAdapterJsonDeserializer(xmlAdapter); |
| XmlAdapterJsonSerializer serializer = new XmlAdapterJsonSerializer(xmlAdapter); |
| SimpleDeserializers deserializers = new SimpleDeserializers(); |
| deserializers.addDeserializer(a.type(), deserializer); |
| deserializerFactory.withAdditionalDeserializers(deserializers); |
| serializerFactory.addGenericMapping(a.type(), serializer); |
| StdDeserializerProvider deserializerProvider = |
| new StdDeserializerProvider(deserializerFactory); |
| mapper = new ObjectMapper(); |
| mapper.registerModule(new JsonOrgModule()); |
| |
| mapper.setSerializerFactory(serializerFactory); |
| mapper.setDeserializerProvider(deserializerProvider); |
| } |
| } |
| } |
| } |
| } |
| if (cls != null && mapper == null) { |
| return MAPPER; |
| } |
| if (mapper == null) { |
| mapper = new ObjectMapper(); |
| mapper.registerModule(new JsonOrgModule()); |
| } |
| // Let's honor the Jackson annotations first |
| AnnotationIntrospector primary = new JacksonAnnotationIntrospector() { |
| |
| @Override |
| public Object findFilterId(AnnotatedClass annotatedClass) { |
| Object filterId = super.findFilterId(annotatedClass); |
| return filterId == null ? TUSCANY_FILTER : filterId; |
| } |
| |
| }; |
| AnnotationIntrospector secondary = new JaxbAnnotationIntrospector(); |
| AnnotationIntrospector pair = new AnnotationIntrospector.Pair(primary, secondary); |
| mapper.setDeserializationConfig(mapper.getDeserializationConfig().withAnnotationIntrospector(pair) |
| .without(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES) |
| .withDateFormat(StdDateFormat.getBlueprintISO8601Format())); |
| mapper.setSerializationConfig(mapper.getSerializationConfig().withAnnotationIntrospector(pair) |
| .withSerializationInclusion(JsonSerialize.Inclusion.NON_NULL) |
| .withDateFormat(StdDateFormat.getBlueprintISO8601Format())); |
| |
| mapper.setFilters(new SimpleFilterProvider().addFilter(TUSCANY_FILTER, DEFAULT_FILTER)); |
| return mapper; |
| } |
| |
| public static JsonFactory getJsonFactory() { |
| return FACTORY; |
| } |
| |
| public static String toString(JsonNode node) { |
| try { |
| return MAPPER.writeValueAsString(node); |
| } catch (IOException e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| public static String toString(JsonParser parser) { |
| try { |
| JsonFactory jsonFactory = getJsonFactory(); |
| StringWriter sw = new StringWriter(); |
| JsonGenerator generator = jsonFactory.createJsonGenerator(sw); |
| JsonNode node = parser.readValueAs(JsonNode.class); |
| generator.writeTree(node); |
| return sw.toString(); |
| } catch (IOException e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| public static JsonParser createJsonParser(String content) { |
| JsonFactory jsonFactory = getJsonFactory(); |
| try { |
| return jsonFactory.createJsonParser(content); |
| } catch (IOException e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| public static JsonParser createJsonParser(InputStream content) { |
| JsonFactory jsonFactory = getJsonFactory(); |
| try { |
| return jsonFactory.createJsonParser(content); |
| } catch (IOException e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| public static JsonParser createJsonParser(Reader content) { |
| JsonFactory jsonFactory = getJsonFactory(); |
| try { |
| return jsonFactory.createJsonParser(content); |
| } catch (IOException e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| public static void write(JsonNode node, OutputStream out) { |
| try { |
| JsonFactory jsonFactory = getJsonFactory(); |
| JsonGenerator generator = jsonFactory.createJsonGenerator(out, JsonEncoding.UTF8); |
| generator.writeTree(node); |
| } catch (IOException e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| public static void write(JsonParser parser, OutputStream out) { |
| try { |
| JsonFactory jsonFactory = getJsonFactory(); |
| JsonGenerator generator = jsonFactory.createJsonGenerator(out, JsonEncoding.UTF8); |
| JsonNode node = parser.readValueAs(JsonNode.class); |
| generator.writeTree(node); |
| } catch (IOException e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| public static JSONObject read(InputStream is) throws IOException { |
| try { |
| return MAPPER.readValue(is, JSONObject.class); |
| } catch (Exception e) { |
| throw new IOException(e); |
| } |
| } |
| |
| /** |
| * Read from String into a org.json.JSONObject |
| * @param json |
| * @return |
| */ |
| public static JSONObject read(String json) { |
| try { |
| return MAPPER.readValue(json, JSONObject.class); |
| } catch (Exception e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| public static void write(JSONObject json, OutputStream out) throws IOException { |
| try { |
| MAPPER.writeValue(out, json); |
| } catch (Exception e) { |
| throw new IOException(e); |
| } |
| } |
| |
| public static String write(JSONObject json) throws IOException { |
| try { |
| return MAPPER.writeValueAsString(json); |
| } catch (Exception e) { |
| throw new IOException(e); |
| } |
| } |
| |
| public static FilterProvider configureFilterProvider(TransformationContext context) { |
| SimpleBeanPropertyFilter filter = DEFAULT_FILTER; |
| if (context != null) { |
| Set<String> included = (Set<String>)context.getMetadata().get(INCLUDED_FIELDS); |
| Set<String> excluded = (Set<String>)context.getMetadata().get(EXCLUDED_FIELDS); |
| // Class<?> type = context.getSourceDataType() == null ? null : context.getSourceDataType().getPhysical(); |
| filter = new TuscanyBeanPropertyFilter(included, excluded); |
| } |
| FilterProvider filters = new SimpleFilterProvider().addFilter(TUSCANY_FILTER, filter); |
| return filters; |
| } |
| |
| private static class TuscanyBeanPropertyFilter extends SimpleBeanPropertyFilter { |
| private List<String> includedFields; |
| private List<String> excludedFields; |
| |
| private Stack<String> path = new Stack<String>(); |
| |
| public TuscanyBeanPropertyFilter(Set<String> includedFields, Set<String> excludedFields) { |
| if (includedFields == null) { |
| includedFields = new HashSet<String>(); |
| includedFields.add(""); // Allows any fields |
| } |
| if (excludedFields == null) { |
| excludedFields = Collections.emptySet(); |
| } |
| this.includedFields = new ArrayList<String>(includedFields); |
| Collections.sort(this.includedFields, Collections.reverseOrder()); |
| this.excludedFields = new ArrayList<String>(excludedFields); |
| Collections.sort(this.excludedFields, Collections.reverseOrder()); |
| } |
| |
| @Override |
| public void serializeAsField(Object bean, |
| JsonGenerator jgen, |
| SerializerProvider provider, |
| BeanPropertyWriter writer) throws Exception { |
| path.push(writer.getName()); |
| String fname = getFullName(path); |
| try { |
| // System.out.println(path); |
| if (isAllowed(fname, includedFields, excludedFields)) { |
| // Matching includes, write |
| writer.serializeAsField(bean, jgen, provider); |
| } |
| } finally { |
| path.pop(); |
| } |
| } |
| |
| /** |
| * Check the target string is a prefix of the source separated by . |
| * @param source |
| * @param target |
| * @return |
| */ |
| private boolean matches(String source, String target) { |
| int index = source.indexOf(target); |
| if (index == -1) { |
| return false; |
| } |
| if (target.length() == source.length() || source.charAt(target.length()) == '.') { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Check if the path matches the one of the patterns |
| * @param path |
| * @param patterns |
| * @param included |
| * @return |
| */ |
| private boolean isAllowed(String fullName, List<String> included, List<String> excluded) { |
| String ex = null; |
| for (String p : excluded) { |
| if (matches(fullName, p)) { |
| ex = p; |
| break; |
| } |
| } |
| for (String p : included) { |
| if (matches(fullName, p) // If the parent element is included |
| || matches(p, fullName) // If one of the child elements is included |
| ) { |
| if (ex != null && ex.length() > p.length()) { |
| // We already have an exclusion pattern that's more matching |
| return false; |
| } |
| return true; |
| } |
| } |
| return ex == null && included.contains(""); |
| } |
| |
| private String getFullName(Stack<String> path) { |
| StringBuilder builder = new StringBuilder(); |
| for (int i = 0; i < path.size(); i++) { |
| builder.append(path.get(i)); |
| if (i != path.size() - 1) { |
| builder.append("."); |
| } |
| } |
| String qname = builder.toString(); |
| return qname; |
| } |
| |
| } |
| |
| } |