blueprint.sdk.core.jayson.Jayson Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of blueprint-sdk Show documentation
Show all versions of blueprint-sdk Show documentation
Personal library for Java development. Deployed on OSSRH for Apache Maven.
The newest version!
/*
License:
blueprint-sdk is licensed under the terms of Eclipse Public License(EPL) v1.0
(http://www.eclipse.org/legal/epl-v10.html)
Distribution:
Maven Central - https://search.maven.org/artifact/io.github.lempel/blueprint-sdk
MVN Repository - https://mvnrepository.com/artifact/io.github.lempel/blueprint-sdk
*/
package blueprint.sdk.core.jayson;
import blueprint.sdk.util.TypeChecker;
import blueprint.sdk.util.Validator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.*;
/**
* JSON wrapper built on top of Jackson.
* Provides E4X like approach.
*
* Basically, gives ways to access JSON with path.
* ex 1) jayson.json("profile.characters");
* ex 2) jayson.json("profile.characters.simon.id", "simon's id");
*
* Setting another child JSON is allowed but must be in serialized form.
* ex 3) jayson.json("profile.characters.simon", "{\"comment\":\"just been replaced\"}");
* Also, JSON array can be set.
* ex 4) jayson.json("profile.characters.simon.items", "[{\"name\":\"item_1\"}, {\"name\":\"item_2\"}, {\"name\":\"item_3\"}]");
*
* Path can contain variables.
* Variables must be notated with braces.
* ex 5) profile.characters[{name}]
* Variable must be set with {@link Jayson#let(String, Object)}.
* Variable name must be consists of alphabets, numbers and under score.
* ex 6) jayson.let("name", "simon");
*
* Children can be accessed with either brackets or dots.
* ex 7) profile.characters.{name}.items.{i}.prop.{j}.value
* ex 8) profile[characters][{name}][items][{i}][prop][{j}][value]
*
*
* @author [email protected]
* @since 2021. 4. 20.
*/
public class Jayson extends HashMap {
protected final Map values = new HashMap<>();
/**
* Constructor
*/
public Jayson() {
super();
}
/**
* Constructor
*
* @param json JSON string
* @throws IOException Jackson's Exception
*/
public Jayson(String json) throws IOException {
super();
putAll(parse(json));
}
/**
* Serialize Map as JSON String
*
* @param target Map or Jayson
* @return JSON String
* @throws JsonProcessingException Jackson's Exception
*/
public static String stringify(Map target) throws JsonProcessingException {
return stringify(target, false);
}
/**
* Serialize Map as JSON String
*
* @param target Map or Jayson
* @param pretty pretty format
* @return JSON String
* @throws JsonProcessingException Jackson's Exception
*/
public static String stringify(Map target, boolean pretty) throws JsonProcessingException {
String ret;
ObjectMapper mapper = new ObjectMapper();
if (pretty) {
ret = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(target);
} else {
ret = mapper.writeValueAsString(target);
}
return ret;
}
// TODO Map 를 argument 로 받는 constructor 구현
/**
* Serialize as JSON String
*
* @return JSON String
* @throws JsonProcessingException Jackson's Exception
*/
public String stringify() throws JsonProcessingException {
return stringify(this, false);
}
/**
* Serialize as JSON String
*
* @param pretty pretty format
* @return JSON String
* @throws JsonProcessingException Jackson's Exception
*/
public String stringify(boolean pretty) throws JsonProcessingException {
return stringify(this, pretty);
}
/**
* Parse JSON String
*
* @param jsonStr JSON string
* @return parsed Jayson
* @throws IOException Jackson's Exception
*/
public static Jayson parse(String jsonStr) throws IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(jsonStr, Jayson.class);
}
/**
* Get current value of designated varaible.
*
* @param name variable name
* @return current value
*/
public Object let(String name) {
return values.get(name);
}
/**
* Set new value for a variable
*
* @param name variable name
* @param value variable value
*/
public void let(String name, Object value) {
values.put(name, value);
}
/**
* Evaluates given path and returns designated value
*
* @param path target
* @return designated value
* @throws JaysonException invalid path
*/
public Object json(String path) throws JaysonException {
return json(path, false, null);
}
/**
* Evaluates given path and sets new value
*
* @param path target
* @param value new value
* @return designated value
* @throws JaysonException invalid path
*/
public Object json(String path, Object value) throws JaysonException {
// TODO 자동생성 flag 를 두고, map 에 한정해서 자동으로 child 를 생성할수 있게 하자
return json(path, true, value);
}
/**
* Evaluates given path and do get or set
*
* @param path target
* @param doSet true: do set
* @param value new value
* @return designated value
* @throws JaysonException invalid path
*/
protected Object json(String path, boolean doSet, Object value) throws JaysonException {
Object ret;
String[] tokens = tokenizePath(path);
StringBuilder processed = new StringBuilder();
Object target = this;
Object lastTarget = null;
String lastToken = null;
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i];
if (token.isEmpty()) {
continue;
}
// check variables
if (token.matches("\\{[a-z|A-Z|\\_|\\d]+\\}")) {
String name = token.substring(1, token.length() - 1);
if (values.containsKey(name)) {
token = String.valueOf(values.get(name));
} else {
throw new JaysonException(token + " is not defined - " + processed);
}
}
if (target instanceof List) {
// search List
if (TypeChecker.isInteger(token)) {
List list = (List) target;
int index = Integer.parseInt(token);
if (index < list.size()) {
lastTarget = target;
lastToken = token;
target = list.get(index);
// compile path for messages
processed.append("[").append(token).append("]");
} else {
throw new JaysonException(index + " is out of bounds - " + processed);
}
} else {
// creating new child on List is not allowed
throw new JaysonException(token + " is not an index - " + processed);
}
} else {
// search Map
Map map = (Map) target;
if (map.containsKey(token)) {
lastTarget = target;
lastToken = token;
target = map.get(token);
// compile path for messages
if (processed.length() > 0) {
processed.append(".");
}
processed.append(token);
} else {
if (doSet && i == tokens.length - 1) {
// creating new child on Map
lastTarget = target;
lastToken = token;
} else {
throw new JaysonException(token + " is not defined - " + processed);
}
}
}
}
if (doSet) {
Object actualValue = value;
if (value instanceof String) {
String valueStr = ((String) value).trim();
if (Validator.isNotEmpty(valueStr) && valueStr.startsWith("{") && valueStr.endsWith("}")) {
// parse value if it's an JSON string
try {
actualValue = Jayson.parse(valueStr);
} catch (IOException e) {
throw new JaysonException("value is not a proper jSON - " + processed);
}
} else if (Validator.isNotEmpty(valueStr) && valueStr.startsWith("[") && valueStr.endsWith("]")) {
// parse value if it's an JSON array
try {
Jayson newChild = Jayson.parse("{\"array\":" + valueStr + "}");
actualValue = newChild.json("array");
} catch (IOException e) {
throw new JaysonException("\"" + value + "\" is not a proper JSON array - " + processed);
}
}
}
if (TypeChecker.isInteger(lastToken) && lastTarget instanceof List) {
((List) lastTarget).set(Integer.parseInt(lastToken), actualValue);
} else {
((Map) lastTarget).put(lastToken, actualValue);
}
ret = actualValue;
} else {
ret = target;
}
return ret;
}
/**
* Push a value to target array
*
* @param path target array
* @param value value to push
* @return target array
* @throws JaysonException invalid target
*/
public Object push(String path, Object value) throws JaysonException {
List ret = new ArrayList();
Object actualValue = value;
if (value instanceof String) {
String valueStr = ((String) value).trim();
if (Validator.isNotEmpty(valueStr) && valueStr.startsWith("{") && valueStr.endsWith("}")) {
// parse value if it's an JSON string
try {
actualValue = Jayson.parse(valueStr);
} catch (IOException e) {
throw new JaysonException("value is not a proper jSON - " + value);
}
} else if (Validator.isNotEmpty(valueStr) && valueStr.startsWith("[") && valueStr.endsWith("]")) {
// parse value if it's an JSON array
try {
Jayson newChild = Jayson.parse("{\"array\":" + valueStr + "}");
actualValue = newChild.json("array");
} catch (IOException e) {
throw new JaysonException("\"" + value + "\" is not a proper JSON array - " + value);
}
}
}
// get as a list
List list = null;
Object arr = json(path);
if (arr instanceof Object[]) {
list = Arrays.asList(arr);
} else if (arr instanceof List) {
list = (List) arr;
} else {
throw new JaysonException("target is not an array - " + path);
}
// append value
ret.addAll(list);
ret.add(actualValue);
// replace
json(path, ret);
return ret;
}
/**
* See if given path exists or not
*
* @param path target
* @return true: exists
*/
public boolean exists(String path) {
boolean ret = false;
try {
json(path);
ret = true;
} catch (RuntimeException ignored) {
}
return ret;
}
/**
* Gets the length of designated array
*
* @param path target
* @return array length
* @throws JaysonException target is not an array
*/
public int length(String path) {
int ret = -1;
Object target = json(path);
if (target instanceof List) {
ret = ((List) target).size();
} else {
throw new JaysonException("not an array - " + path);
}
return ret;
}
/**
* Tokenize given path
*
* @param path target
* @return tokens
*/
protected String[] tokenizePath(String path) {
ArrayList ret = new ArrayList<>();
if (!Validator.isEmpty(path)) {
String[] tokens = path.split("[\\.|\\[|\\]]");
for (String token : tokens) {
if (token.isEmpty()) {
continue;
}
ret.add(token);
}
}
return ret.toArray(new String[0]);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy