/* * SoapUI, Copyright (C) 2004-2016 SmartBear Software * * Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * * http://ec.europa.eu/idabc/eupl * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the Licence for the specific language governing permissions and limitations * under the Licence. */ package com.eviware.soapui.support; import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.internal.PathToken; import com.jayway.jsonpath.internal.PathTokenizer; import com.jayway.jsonpath.internal.filter.PathTokenFilter; import net.sf.json.JSON; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import net.sf.json.groovy.JsonSlurper; import java.lang.reflect.Field; import java.util.LinkedList; import java.util.List; import java.util.Map; import static com.eviware.soapui.support.JsonUtil.isValidJson; public class JsonPathFacade { private String currentJson; private Object jsonObject; public JsonPathFacade(String targetJson) { if (!isValidJson(targetJson)) { throw new IllegalArgumentException("Invalid JSON: " + targetJson); } this.currentJson = targetJson; jsonObject = new JsonSlurper().parseText(targetJson); } public String readStringValue(String jsonPathExpression) { return String.valueOf(readObjectValue(jsonPathExpression)); } public void writeValue(String jsonPathExpression, Object value) { PlainJavaJsonProvider provider = new PlainJavaJsonProvider(); Configuration configuration = Configuration.builder().jsonProvider(provider).build(); jsonObject = provider.parse(currentJson); JsonPath path = JsonPath.compile(jsonPathExpression); LinkedList<PathToken> pathTokens = getPathTokensFrom(path); PathToken endToken = pathTokens.removeLast(); int index = pathTokens.size(); JsonWriteDecorator writeDecorator = new JsonWriteDecorator(provider, index, endToken, value); pathTokens.addLast(writeDecorator); path.read(jsonObject, configuration); jsonObject = MutableValue.FROM_MUTABLE_VALUE.apply(jsonObject); currentJson = buildJsonStringFrom(jsonObject); } private String buildJsonStringFrom(Object sourceObject) { Object json = makeJSONObject(sourceObject); return json instanceof JSON ? ((JSON) json).toString(3) : json.toString(); } private Object makeJSONObject(Object sourceObject) { if (sourceObject instanceof Map) { JSONObject jsonObject = new JSONObject(); Map sourceMap = (Map) sourceObject; for (Object key : sourceMap.keySet()) { jsonObject.put(key, makeJSONObject(sourceMap.get(key))); } return jsonObject; } else if (sourceObject instanceof List) { List sourceList = (List) sourceObject; JSONArray array = new JSONArray(); for (Object element : sourceList) { array.add(makeJSONObject(element)); } return array; } else { return sourceObject; } } private void removeMutableWrappersFrom(JSON jsonObject) { if (jsonObject.isArray()) { JSONArray array = (JSONArray) jsonObject; for (int i = 0; i < array.size(); i++) { array.set(i, removeMutableWrapperFrom(array.get(i))); } } else if (jsonObject instanceof JSONObject) { JSONObject object = (JSONObject) jsonObject; for (Object key : object.keySet()) { object.put(key, removeMutableWrapperFrom(object.get(key))); } } } private Object removeMutableWrapperFrom(Object o) { Object value = MutableValue.extractValueFromMutable(o); if (value instanceof JSON) { removeMutableWrappersFrom((JSON) value); } return value; } private LinkedList<PathToken> getPathTokensFrom(JsonPath jsonPathObject) { try { Field tokenizerField = JsonPath.class.getDeclaredField("tokenizer"); tokenizerField.setAccessible(true); PathTokenizer tokenizer = (PathTokenizer) tokenizerField.get(jsonPathObject); Field pathTokensField = PathTokenizer.class.getDeclaredField("pathTokens"); pathTokensField.setAccessible(true); return (LinkedList<PathToken>) pathTokensField.get(tokenizer); } catch (Exception e) { throw new RuntimeException(e); } } public Object getJSON() { return jsonObject; } public String getCurrentJson() { return currentJson; } public <T> T readObjectValue(String jsonPathExpression) { PlainJavaJsonProvider provider = new PlainJavaJsonProvider(); Configuration configuration = Configuration.builder().jsonProvider(provider).build(); JsonPath jsonPath = JsonPath.compile(jsonPathExpression); return jsonPath.read(jsonObject, configuration); } private class JsonWriteDecorator extends PathToken { private final PlainJavaJsonProvider provider; private final Object value; public JsonWriteDecorator(PlainJavaJsonProvider provider, int index, PathToken endToken, Object value) { super(endToken.getFragment(), index, true); this.provider = provider; this.value = value; } @Override public PathTokenFilter getFilter() { // WORKAROUND: ideally we would use a decorator PathTokenFilter instead, but the PathTokenFilter constructor // is package protected. Unfortunately this entails that we can't reuse the provider after this step. provider.setValueToWrite(value); return super.getFilter(); } } }