/*
* Copyright 2012-2017 the original author or authors.
*
* 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.springframework.boot.json;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.util.StringUtils;
/**
* Really basic JSON parser for when you have nothing else available. Comes with some
* limitations with respect to the JSON specification (e.g. only supports String values),
* so users will probably prefer to have a library handle things instead (Jackson or Snake
* YAML are supported).
*
* @author Dave Syer
* @author Jean de Klerk
* @since 1.2.0
* @see JsonParserFactory
*/
public class BasicJsonParser implements JsonParser {
@Override
public Map<String, Object> parseMap(String json) {
if (json != null) {
json = json.trim();
if (json.startsWith("{")) {
return parseMapInternal(json);
}
}
throw new IllegalArgumentException("Cannot parse JSON");
}
@Override
public List<Object> parseList(String json) {
if (json != null) {
json = json.trim();
if (json.startsWith("[")) {
return parseListInternal(json);
}
}
throw new IllegalArgumentException("Cannot parse JSON");
}
private List<Object> parseListInternal(String json) {
List<Object> list = new ArrayList<>();
json = trimLeadingCharacter(trimTrailingCharacter(json, ']'), '[');
for (String value : tokenize(json)) {
list.add(parseInternal(value));
}
return list;
}
private Object parseInternal(String json) {
if (json.startsWith("[")) {
return parseListInternal(json);
}
if (json.startsWith("{")) {
return parseMapInternal(json);
}
if (json.startsWith("\"")) {
return trimTrailingCharacter(trimLeadingCharacter(json, '"'), '"');
}
try {
return Long.valueOf(json);
}
catch (NumberFormatException ex) {
// ignore
}
try {
return Double.valueOf(json);
}
catch (NumberFormatException ex) {
// ignore
}
return json;
}
private static String trimTrailingCharacter(String string, char c) {
if (string.length() > 0 && string.charAt(string.length() - 1) == c) {
return string.substring(0, string.length() - 1);
}
return string;
}
private static String trimLeadingCharacter(String string, char c) {
if (string.length() > 0 && string.charAt(0) == c) {
return string.substring(1);
}
return string;
}
private Map<String, Object> parseMapInternal(String json) {
Map<String, Object> map = new LinkedHashMap<>();
json = trimLeadingCharacter(trimTrailingCharacter(json, '}'), '{');
for (String pair : tokenize(json)) {
String[] values = StringUtils.trimArrayElements(StringUtils.split(pair, ":"));
String key = trimLeadingCharacter(trimTrailingCharacter(values[0], '"'), '"');
Object value = null;
if (values.length > 0) {
String string = trimLeadingCharacter(
trimTrailingCharacter(values[1], '"'), '"');
value = parseInternal(string);
}
map.put(key, value);
}
return map;
}
private List<String> tokenize(String json) {
List<String> list = new ArrayList<>();
int index = 0;
int inObject = 0;
int inList = 0;
StringBuilder build = new StringBuilder();
while (index < json.length()) {
char current = json.charAt(index);
if (current == '{') {
inObject++;
}
if (current == '}') {
inObject--;
}
if (current == '[') {
inList++;
}
if (current == ']') {
inList--;
}
if (current == ',' && inObject == 0 && inList == 0) {
list.add(build.toString());
build.setLength(0);
}
else {
build.append(current);
}
index++;
}
if (build.length() > 0) {
list.add(build.toString());
}
return list;
}
}