001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.json; 018 019import java.util.ArrayList; 020import java.util.LinkedHashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.springframework.util.StringUtils; 025 026/** 027 * Really basic JSON parser for when you have nothing else available. Comes with some 028 * limitations with respect to the JSON specification (e.g. only supports String values), 029 * so users will probably prefer to have a library handle things instead (Jackson or Snake 030 * YAML are supported). 031 * 032 * @author Dave Syer 033 * @author Jean de Klerk 034 * @author Stephane Nicoll 035 * @since 1.2.0 036 * @see JsonParserFactory 037 */ 038public class BasicJsonParser extends AbstractJsonParser { 039 040 @Override 041 public Map<String, Object> parseMap(String json) { 042 return parseMap(json, this::parseMapInternal); 043 } 044 045 @Override 046 public List<Object> parseList(String json) { 047 return parseList(json, this::parseListInternal); 048 } 049 050 private List<Object> parseListInternal(String json) { 051 List<Object> list = new ArrayList<>(); 052 json = trimLeadingCharacter(trimTrailingCharacter(json, ']'), '['); 053 for (String value : tokenize(json)) { 054 list.add(parseInternal(value)); 055 } 056 return list; 057 } 058 059 private Object parseInternal(String json) { 060 if (json.startsWith("[")) { 061 return parseListInternal(json); 062 } 063 if (json.startsWith("{")) { 064 return parseMapInternal(json); 065 } 066 if (json.startsWith("\"")) { 067 return trimTrailingCharacter(trimLeadingCharacter(json, '"'), '"'); 068 } 069 try { 070 return Long.valueOf(json); 071 } 072 catch (NumberFormatException ex) { 073 // ignore 074 } 075 try { 076 return Double.valueOf(json); 077 } 078 catch (NumberFormatException ex) { 079 // ignore 080 } 081 return json; 082 } 083 084 private static String trimTrailingCharacter(String string, char c) { 085 if (!string.isEmpty() && string.charAt(string.length() - 1) == c) { 086 return string.substring(0, string.length() - 1); 087 } 088 return string; 089 } 090 091 private static String trimLeadingCharacter(String string, char c) { 092 if (!string.isEmpty() && string.charAt(0) == c) { 093 return string.substring(1); 094 } 095 return string; 096 } 097 098 private Map<String, Object> parseMapInternal(String json) { 099 Map<String, Object> map = new LinkedHashMap<>(); 100 json = trimLeadingCharacter(trimTrailingCharacter(json, '}'), '{'); 101 for (String pair : tokenize(json)) { 102 String[] values = StringUtils.trimArrayElements(StringUtils.split(pair, ":")); 103 String key = trimLeadingCharacter(trimTrailingCharacter(values[0], '"'), '"'); 104 Object value = parseInternal(values[1]); 105 map.put(key, value); 106 } 107 return map; 108 } 109 110 private List<String> tokenize(String json) { 111 List<String> list = new ArrayList<>(); 112 int index = 0; 113 int inObject = 0; 114 int inList = 0; 115 boolean inValue = false; 116 boolean inEscape = false; 117 StringBuilder build = new StringBuilder(); 118 while (index < json.length()) { 119 char current = json.charAt(index); 120 if (inEscape) { 121 build.append(current); 122 index++; 123 inEscape = false; 124 continue; 125 } 126 if (current == '{') { 127 inObject++; 128 } 129 if (current == '}') { 130 inObject--; 131 } 132 if (current == '[') { 133 inList++; 134 } 135 if (current == ']') { 136 inList--; 137 } 138 if (current == '"') { 139 inValue = !inValue; 140 } 141 if (current == ',' && inObject == 0 && inList == 0 && !inValue) { 142 list.add(build.toString()); 143 build.setLength(0); 144 } 145 else if (current == '\\') { 146 inEscape = true; 147 } 148 else { 149 build.append(current); 150 } 151 index++; 152 } 153 if (build.length() > 0) { 154 list.add(build.toString()); 155 } 156 return list; 157 } 158 159}