Here is how I would solve this problem if I needed fast low level pure Java solution working in JSON token stream way provided by FastXML/Jackson library:
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
public class JsonTokenStreamTest {
public static void main(String[] args) throws IOException {
String inputJson = "[{},{\"k1\":null,\"k2\":true,\"k3\":\"v3\",\"k/4\":[123, 45],\"k~5\":6.7}]";
Reader r = new StringReader(inputJson);
StringWriter w = new StringWriter();
substitute(r, "/1/k~14/0", 124, w); // "~1" in json-pathes means "/"
String outputJson = w.toString();
System.out.println(outputJson);
}
public static void substitute(Reader r, String path, Object value,
Writer w) throws IOException {
if (path.length() > 0 && !path.startsWith("/"))
path = "/" + path;
if (path.endsWith("/"))
path = path.substring(0, path.length() - 1);
JsonFactory jfactory = new JsonFactory();
JsonParser jParser = jfactory.createParser(r);
JsonGenerator jGenerator = jfactory.createGenerator(w);
StringBuilder curTextPath = new StringBuilder();
List<Object> curObjPath = new ArrayList<Object>();
while (true) {
JsonToken jt = jParser.nextToken();
if (jt == null)
break;
if (curObjPath.size() > 0) {
Object item = curObjPath.get(curObjPath.size() - 1);
if (item instanceof Integer) { // Increment array index in path
item = 1 + (Integer)item;
curObjPath.set(curObjPath.size() - 1, item);
updateLastPathItem(item, curTextPath);
}
}
if (jt == JsonToken.START_ARRAY || jt == JsonToken.START_OBJECT) {
if (jt == JsonToken.START_ARRAY) {
jGenerator.writeStartArray();
curObjPath.add(-1);
} else if (jt == JsonToken.START_OBJECT) {
jGenerator.writeStartObject();
curObjPath.add("");
}
curTextPath.append("/");
} else if (jt == JsonToken.END_ARRAY || jt == JsonToken.END_OBJECT) {
if (jt == JsonToken.END_ARRAY) {
jGenerator.writeEndArray();
} else if (jt == JsonToken.END_OBJECT) {
jGenerator.writeEndObject();
}
curObjPath.remove(curObjPath.size() - 1);
curTextPath.delete(curTextPath.lastIndexOf("/"), curTextPath.length());
} else if (jt == JsonToken.FIELD_NAME) {
String item = jParser.getText();
jGenerator.writeFieldName(item);
curObjPath.set(curObjPath.size() - 1, item);
updateLastPathItem(item, curTextPath);
} else {
if (path.equals(curTextPath.toString())) {
if (value == null) {
jGenerator.writeNull();
} else if (value instanceof Boolean) {
jGenerator.writeBoolean((Boolean)value);
} else if (value instanceof Short) {
jGenerator.writeNumber((Short)value);
} else if (value instanceof Integer) {
jGenerator.writeNumber((Integer)value);
} else if (value instanceof Long) {
jGenerator.writeNumber((Long)value);
} else if (value instanceof BigInteger) {
jGenerator.writeNumber((BigInteger)value);
} else if (value instanceof Float) {
jGenerator.writeNumber((Float)value);
} else if (value instanceof Double) {
jGenerator.writeNumber((Double)value);
} else if (value instanceof BigDecimal) {
jGenerator.writeNumber((BigDecimal)value);
} else if (value instanceof String) {
jGenerator.writeString((String)value);
} else {
throw new IllegalStateException("Unsupported type of substitution: " +
value.getClass().getName());
}
} else if (jt == JsonToken.VALUE_FALSE || jt == JsonToken.VALUE_TRUE) {
jGenerator.writeBoolean(jt == JsonToken.VALUE_TRUE);
} else if (jt == JsonToken.VALUE_NULL) {
jGenerator.writeNull();
} else if (jt == JsonToken.VALUE_STRING) {
jGenerator.writeString(jParser.getText());
} else if (jt == JsonToken.VALUE_NUMBER_INT) {
jGenerator.writeNumber(jParser.getBigIntegerValue());
} else if (jt == JsonToken.VALUE_NUMBER_FLOAT) {
jGenerator.writeNumber(jParser.getDecimalValue());
} else {
throw new IllegalStateException("Unsupported token type: " + jt.name());
}
}
}
jParser.close();
jGenerator.close();
}
private static void updateLastPathItem(Object item, StringBuilder sb) {
int pos = sb.lastIndexOf("/");
if (pos < 0)
throw new IllegalStateException("Unexpected internal state: " + sb);
String str = String.valueOf(item);
sb.replace(pos + 1, sb.length(), str.replace("~", "~0").replace("/", "~1"));
}
}