/* * Copyright (c) 2011-2014 Pivotal Software, Inc. * * 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.camunda.bpm.extension.reactor.projectreactor.selector; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.Filter; import com.jayway.jsonpath.InvalidJsonException; import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.TypeRef; import com.jayway.jsonpath.spi.json.JsonProvider; import com.jayway.jsonpath.spi.mapper.MappingProvider; import org.camunda.bpm.extension.reactor.projectreactor.io.buffer.Buffer; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * @author Jon Brisbin */ public class JsonPathSelector extends ObjectSelector<Object, JsonPath> { // Only need one of these private static ObjectMapper MAPPER = new ObjectMapper(); private final ObjectMapper mapper; private final Configuration jsonPathConfig; public JsonPathSelector(ObjectMapper mapper, String jsonPath, Filter... filters) { super(JsonPath.compile(jsonPath, filters)); this.mapper = mapper; this.jsonPathConfig = Configuration.builder().jsonProvider(new Jackson2JsonProvider(mapper)).build(); } public JsonPathSelector(String jsonPath, Filter... filters) { this(MAPPER, jsonPath, filters); } public static Selector J(String jsonPath, Filter... filters) { return jsonPathSelector(jsonPath, filters); } public static Selector jsonPathSelector(String jsonPath, Filter... filters) { return new JsonPathSelector(jsonPath, filters); } @Override public boolean matches(Object key) { if (null == key) { return false; } Object result = read(key); if (null == result) { return false; } Class<?> type = result.getClass(); if (Collection.class.isAssignableFrom(type)) { return ((Collection) result).size() > 0; } else if (Map.class.isAssignableFrom(type)) { return ((Map) result).size() > 0; } else if (JsonNode.class.isAssignableFrom(type)) { return ((JsonNode) result).size() > 0; } else { return true; } } private Object read(Object key) { Class<?> type = key.getClass(); if (type == String.class) { return getObject().read((String) key, jsonPathConfig); } else if (type == byte[].class) { return getObject().read(Buffer.wrap((byte[]) key).asString(), jsonPathConfig); } else if (type == Buffer.class) { return getObject().read(((Buffer) key).asString(), jsonPathConfig); } else if (JsonNode.class.isAssignableFrom(type)) { return getObject().read(key, jsonPathConfig); } else { return getObject().read(mapper.convertValue(key, Object.class), jsonPathConfig); } } @SuppressWarnings("unchecked") private static class Jackson2JsonProvider implements JsonProvider, MappingProvider { private final ObjectMapper mapper; private Jackson2JsonProvider(ObjectMapper mapper) { this.mapper = mapper; } @Override public Object parse(String json) throws InvalidJsonException { try { return mapper.readValue(json, Object.class); } catch (IOException e) { throw new InvalidJsonException(e.getMessage(), e); } } @Override public Object parse(InputStream jsonStream, String charset) throws InvalidJsonException { try { return mapper.readValue(new InputStreamReader(jsonStream, charset), Object.class); } catch (IOException e) { throw new IllegalStateException(e); } } @Override public String toJson(Object obj) { try { return mapper.writeValueAsString(obj); } catch (JsonProcessingException e) { throw new IllegalStateException(e.getMessage(), e); } } @Override public Object createMap() { return new HashMap(); } @Override public Iterable createArray() { return new ArrayList(); } @Override public Object getArrayIndex(Object obj, int idx) { return getProperty(obj, idx); } @Override public Object getArrayIndex(Object obj, int idx, boolean unwrap) { Object o = getProperty(obj, idx); if (o instanceof JsonNode && unwrap) { return unwrap((JsonNode) o); } else { return o; } } @Override public void setArrayIndex(Object array, int idx, Object newValue) { setProperty(array, idx, newValue); } @Override public Object getMapValue(Object obj, String key) { return getProperty(obj, key); } @Override public void removeProperty(Object obj, Object key) { setProperty(obj, key, null); } @Override public <T> T map(Object source, Class<T> targetType, Configuration configuration) { return mapper.convertValue(source, targetType); } @Override public <T> T map(Object source, TypeRef<T> targetType, Configuration configuration) { if (targetType.getType() instanceof Class) { return mapper.convertValue(source, (Class<T>) targetType.getType()); } else { throw new IllegalArgumentException("Cannot convert to " + targetType); } } @Override public boolean isArray(Object obj) { return (obj instanceof List || obj instanceof ArrayNode); } @Override public boolean isMap(Object obj) { return (obj instanceof Map || obj instanceof ObjectNode); } @Override public int length(Object obj) { if (obj instanceof List) { return ((List) obj).size(); } else if (obj instanceof Map) { return ((Map) obj).size(); } else if (obj instanceof ArrayNode) { return ((ArrayNode) obj).size(); } else if (obj instanceof ObjectNode) { return ((ObjectNode) obj).size(); } else { return length(mapper.convertValue(obj, JsonNode.class)); } } @Override public Iterable<Object> toIterable(Object obj) { if (obj instanceof List || obj instanceof JsonNode) { return (Iterable<Object>) obj; } else if (obj instanceof Map) { return ((Map) obj).values(); } else { return toIterable(mapper.convertValue(obj, JsonNode.class)); } } @Override public Collection<String> getPropertyKeys(Object obj) { if (obj instanceof Map) { return ((Map) obj).keySet(); } else if (obj instanceof List) { List l = (List) obj; List<String> keys = new ArrayList<String>(l.size()); for (Object o : l) { keys.add(String.valueOf(o)); } return keys; } else if (obj instanceof ObjectNode) { ObjectNode node = (ObjectNode) obj; List<String> keys = new ArrayList<String>(node.size()); Iterator<String> iter = node.fieldNames(); while (iter.hasNext()) { keys.add(iter.next()); } return keys; } else if (obj instanceof ArrayNode) { ArrayNode node = (ArrayNode) obj; List<String> keys = new ArrayList<String>(node.size()); int len = node.size(); for (int i = 0; i < len; i++) { keys.add(String.valueOf(node.get(i))); } return keys; } else { return getPropertyKeys(mapper.convertValue(obj, JsonNode.class)); } } @Override public void setProperty(Object obj, Object key, Object value) { if (obj instanceof Map) { ((Map) obj).put(key, value); } else if (obj instanceof List) { int idx = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString()); ((List) obj).add(idx, value); } else if (obj instanceof ObjectNode) { ((ObjectNode) obj).set(key.toString(), (JsonNode) value); } else if (obj instanceof ArrayNode) { int idx = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString()); ((ArrayNode) obj).set(idx, (JsonNode) value); } else { setProperty(mapper.convertValue(obj, JsonNode.class), key, value); } } @Override public Object unwrap(Object object) { if (object instanceof JsonNode) { return unwrap((JsonNode) object); } else { return object; } } private Object getProperty(Object obj, Object key) { if (obj instanceof Map) { return ((Map) obj).get(key); } else if (obj instanceof List) { int idx = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString()); return ((List) obj).get(idx); } else if (obj instanceof ObjectNode) { return unwrap(((ObjectNode) obj).get(key.toString())); } else if (obj instanceof ArrayNode) { int idx = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString()); return unwrap(((ArrayNode) obj).get(idx)); } else { return getProperty(mapper.convertValue(obj, JsonNode.class), key); } } private Object unwrap(JsonNode node) { if (node.isValueNode()) { if (node.isNumber()) { return node.numberValue(); } else if (node.isTextual()) { return node.asText(); } else { return node; } } else { return node; } } } }