JsonUtilities

Json Utilities:

Diese Klasse beinhaltet Json Utilities

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
package de.soderer.utilities.json;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

public class JsonUtilities {
  public static JsonObject parseJsonData(String jsonDataString, boolean throwExceptionOnError) throws Exception {
    try {
      char[] jsonData = jsonDataString.toCharArray();
      int startIndex = 0;
      for (startIndex = 0; startIndex < jsonData.length; startIndex++) {
        if (Character.isWhitespace(jsonData[startIndex])) {
          // do nothing
        } else if (jsonData[startIndex] == '{') {
          break;
        } else {
          throw new Exception("Unexpected '" + jsonData[startIndex] + "'-sign at index " + startIndex);
        }
      }
      
      JsonObject jsonObject = new JsonObject();
      int endIndexOfObject = jsonObject.parse(jsonData, startIndex + 1) + 1;
      if (endIndexOfObject < jsonData.length) {
        for (int index = endIndexOfObject; index < jsonData.length; index++) {
          if (!Character.isWhitespace(jsonData[index])) {
            throw new Exception("Unexpected '" + jsonData[index] + "'-sign at index " + index);
          }
        }
      }
        
      return jsonObject;
    } catch (Exception e) {
      if (throwExceptionOnError) {
        throw new Exception("Invalid JSON data", e);
      } else {
        return null;
      }
    }
  }
  
  public static boolean isValidUnfinishedJsonValue(String value) {
    if ("t".equalsIgnoreCase(value) || "tr".equalsIgnoreCase(value) || "tru".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value)) {
      return true;
    } else if ("f".equalsIgnoreCase(value) || "fa".equalsIgnoreCase(value) || "fal".equalsIgnoreCase(value) || "fals".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) {
      return true;
    } else if ("n".equalsIgnoreCase(value) || "nu".equalsIgnoreCase(value) || "nul".equalsIgnoreCase(value) || "null".equalsIgnoreCase(value)) {
      return true;
    } else if (Pattern.matches("[+|-]?[0-9]*(\\.[0-9]*)?([e|E][+|-]?[0-9]*)?", value)) {
      return true;
    } else {
      return false;
    }
  }
  
  public static Object getJsonValue(String value) throws Exception {
    if ("true".equalsIgnoreCase(value)) {
      return true;
    } else if ("false".equalsIgnoreCase(value)) {
      return false;
    } else if ("null".equalsIgnoreCase(value)) {
      return null;
    } else if (Pattern.matches("[+|-]?[0-9]*(\\.[0-9]*)?([e|E][+|-]?[0-9]*)?", value)) {
      if (value.contains(".")) {
        return new Double(value);
      } else {
        return new Long(value);
      }
    } else {
      throw new Exception("Invalid value");
    }
  }
  
  public static JsonObject convertXmlDocument(Document xmlDocument, boolean throwExceptionOnError) throws Exception {
    try {
      JsonObject jsonObject = new JsonObject();
      jsonObject.add(xmlDocument.getChildNodes().item(0).getNodeName(), convertXmlNode(xmlDocument.getChildNodes().item(0)));
      return jsonObject;
    } catch (Exception e) {
      if (throwExceptionOnError) {
        throw new Exception("Invalid data", e);
      } else {
        return null;
      }
    }
  }
  
  public static JsonObject convertXmlNode(Node xmlNode) {
    JsonObject jsonObject = new JsonObject();
    if (xmlNode.getAttributes() != null && xmlNode.getAttributes().getLength() > 0) {
      for (int attributeIndex = 0; attributeIndex < xmlNode.getAttributes().getLength(); attributeIndex++) {
        Node attributeNode = xmlNode.getAttributes().item(attributeIndex);
        jsonObject.add(attributeNode.getNodeName(), attributeNode.getNodeValue());
      }
    }
    if (xmlNode.getChildNodes() != null && xmlNode.getChildNodes().getLength() > 0) {
      for (int i = 0; i < xmlNode.getChildNodes().getLength(); i++) {
        Node childNode = xmlNode.getChildNodes().item(i);
        if (childNode.getNodeType() == Node.TEXT_NODE) {
          if (StringUtils.isNotBlank(childNode.getNodeValue())) {
            jsonObject.add("text", childNode.getNodeValue());
          }
        } else if (childNode.getNodeType() == Node.COMMENT_NODE) {
          // do nothing
        } else if (childNode.getChildNodes().getLength() == 1 && childNode.getChildNodes().item(0).getNodeType() == Node.TEXT_NODE) {
          // only one textnode under this node
          jsonObject.add(childNode.getNodeName(), childNode.getChildNodes().item(0).getNodeValue());
        } else {
          Node xmlSubNode = childNode;
          JsonObject nodeJsonObject = convertXmlNode(xmlSubNode);
          if (nodeJsonObject != null) {
            jsonObject.add(xmlSubNode.getNodeName(), nodeJsonObject);
          }
        }
      }
    }
    return jsonObject;
  }
  
  public static Document convertToXmlDocument(JsonObject jsonObject, boolean useAttributes) throws Exception {
    try {
      DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
      DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
      Document xmlDocument = documentBuilder.newDocument();
      xmlDocument.setXmlStandalone(true);
      List<Node> mainNodes = convertToXmlNodes(jsonObject, xmlDocument, useAttributes);
      if (mainNodes == null || mainNodes.size() < 1) {
        throw new Exception("No data found");
      } else if (mainNodes.size() == 1) {
        xmlDocument.appendChild(mainNodes.get(0));
      } else {
        Node rootNode = xmlDocument.createElement("root");
        for (Node subNode : mainNodes) {
          if (subNode instanceof Attr) {
            rootNode.getAttributes().setNamedItem(subNode);
          } else {
            rootNode.appendChild(subNode);
          }
        }
        xmlDocument.appendChild(rootNode);
      }
      return xmlDocument;
    } catch (Exception e) {
      throw new Exception("Invalid data", e);
    }
  }
  
  public static List<Node> convertToXmlNodes(JsonObject jsonObject, Document xmlDocument, boolean useAttributes) {
    List<Node> list = new ArrayList<Node>();
    
    for (String key : jsonObject.keySet()) {
      Object subItem = jsonObject.get(key);
      if (subItem instanceof JsonObject) {
        Node newNode = xmlDocument.createElement(key);
        list.add(newNode);
        for (Node subNode : convertToXmlNodes((JsonObject) subItem, xmlDocument, useAttributes)) {
          if (subNode instanceof Attr) {
            newNode.getAttributes().setNamedItem(subNode);
          } else {
            newNode.appendChild(subNode);
          }
        }
      } else if (subItem instanceof JsonArray) {
        for (Node subNode : convertToXmlNodes((JsonArray) subItem, key, xmlDocument, useAttributes)) {
          list.add(subNode);
        }
      } else if (useAttributes) {
        Attr newAttr = xmlDocument.createAttribute(key);
        newAttr.setNodeValue(subItem.toString());
        list.add(newAttr);
      } else {
        Node newNode = xmlDocument.createElement(key);
        list.add(newNode);
        newNode.setTextContent(subItem.toString());
      }
    }
    
    return list;
  }
  
  public static List<Node> convertToXmlNodes(JsonArray jsonArray, String nodeName, Document xmlDocument, boolean useAttributes) {
    List<Node> list = new ArrayList<Node>();
    
    if (jsonArray.size() > 0) {
      for (Object subItem : jsonArray) {
        if (subItem instanceof JsonObject) {
          Node newNode = xmlDocument.createElement(nodeName);
          list.add(newNode);
          for (Node subNode : convertToXmlNodes((JsonObject) subItem, xmlDocument, useAttributes)) {
            if (subNode instanceof Attr) {
              newNode.getAttributes().setNamedItem(subNode);
            } else {
              newNode.appendChild(subNode);
            }
          }
        } else if (subItem instanceof JsonArray) {
          Node newNode = xmlDocument.createElement(nodeName);
          list.add(newNode);
          for (Node subNode : convertToXmlNodes((JsonArray) subItem, nodeName, xmlDocument, useAttributes)) {
            newNode.appendChild(subNode);
          }
        } else {
          Node newNode = xmlDocument.createElement(nodeName);
          list.add(newNode);
          newNode.setTextContent(subItem.toString());
        }
      }
    } else {
      Node newNode = xmlDocument.createElement(nodeName);
      list.add(newNode);
    }
    
    return list;
  }
}

Dies ist eine Hilfs-Klasse für JsonUtilities

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
package de.soderer.utilities.json;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import de.soderer.utilities.TextUtilities;

public class JsonObject {
  private Map<String, Object> properties = new LinkedHashMap<String, Object>();
  
  public void add(String key, JsonObject object) {
    add(key, (Object) object);
  }
  
  public void add(String key, JsonArray array) {
    add(key, (Object) array);
  }
  
  public void add(String key, String value) {
    add(key, (Object) value);
  }
  
  public void add(String key, Number value) {
    add(key, (Object) value);
  }
  
  public void add(String key, Boolean value) {
    add(key, (Object) value);
  }

  public void addNullValue(String key) {
    add(key, (Object) null);
  }
  
  private void add(String key, Object object) {
    if (properties.containsKey(key)) {
      Object oldObject = properties.get(key);
      if (oldObject instanceof JsonArray) {
        if (object == null) {
          ((JsonArray) oldObject).addNullValue();
        } else if (object instanceof JsonObject) {
          ((JsonArray) oldObject).add((JsonObject) object);
        } else if (object instanceof JsonArray) {
          ((JsonArray) oldObject).add((JsonArray) object);
        } else if (object instanceof String) {
          ((JsonArray) oldObject).add((String) object);
        } else if (object instanceof Number) {
          ((JsonArray) oldObject).add((Number) object);
        } else if (object instanceof Boolean) {
          ((JsonArray) oldObject).add((Boolean) object);
        }
      } else {
        JsonArray replacementArray = new JsonArray();
        properties.put(key, replacementArray);
        if (oldObject == null) {
          replacementArray.addNullValue();
        } else if (oldObject instanceof JsonObject) {
          replacementArray.add((JsonObject) oldObject);
        } else if (oldObject instanceof JsonArray) {
          replacementArray.add((JsonArray) oldObject);
        } else if (oldObject instanceof String) {
          replacementArray.add((String) oldObject);
        } else if (oldObject instanceof Number) {
          replacementArray.add((Number) oldObject);
        } else if (oldObject instanceof Boolean) {
          replacementArray.add((Boolean) oldObject);
        }
        if (object == null) {
          replacementArray.addNullValue();
        } else if (object instanceof JsonObject) {
          replacementArray.add((JsonObject) object);
        } else if (object instanceof JsonArray) {
          replacementArray.add((JsonArray) object);
        } else if (object instanceof String) {
          replacementArray.add((String) object);
        } else if (object instanceof Number) {
          replacementArray.add((Number) object);
        } else if (object instanceof Boolean) {
          replacementArray.add((Boolean) object);
        }
      }
    } else {
      properties.put(key, object);
    }
  }
  
  public Object remove(String key) {
    return properties.remove(key);
  }
  
  public Object get(String key) {
    return properties.get(key);
  }
  
  public Set<String> keySet() {
    return properties.keySet();
  }
  
  public int size() {
    return properties.size();
  }

  @Override
  public String toString() {
    return toString("\t", " ", "\n");
  }

  public String toString(String indention, String separator, String linebreak) {
    if (properties.size() > 0) {
      boolean isFirstKey = true;
      StringBuilder result = new StringBuilder("{");
      
      for (Object keyObject : properties.keySet()) {
        if (!isFirstKey) {
          result.append(",");
        }
        
        result.append(linebreak);
        String key = (String) keyObject;
        result.append(indention);
        result.append("\"");
        result.append(key.replace("\"", "\\\""));
        result.append("\":");
        result.append(separator);
        Object subItem = properties.get(key);
        if (subItem == null) {
          result.append("null");
        } else if (subItem instanceof JsonObject) {
          result.append(TextUtilities.addLeadingTab(((JsonObject) subItem).toString(indention, separator, linebreak)).trim());
        } else if (subItem instanceof JsonArray) {
          result.append(TextUtilities.addLeadingTab(((JsonArray) subItem).toString(indention, separator, linebreak)).trim());
        } else if (subItem instanceof String) {
          result.append("\"");
          result.append(((String) subItem).replace("\"", "\\\""));
          result.append("\"");
        } else {
          result.append(subItem.toString());
        }
        
        isFirstKey = false;
      }
      
      result.append(linebreak);
      result.append("}");
      result.append(linebreak);
      
      return result.toString();
    } else {
      return "{}";
    }
  }
  
  protected int parse(char[] jsonData, int startIndex) throws Exception {
    try {
      properties.clear();
      
      char previousChar = ' ';
      StringBuilder key = null;
      StringBuilder quotedValue = null;
      StringBuilder unquotedValue = null;
      String keyString = null;
      Object valueObject = null;
      
      for (int index = startIndex; index < jsonData.length; index++) {
        char charItem = jsonData[index];
        
        if (charItem == '\\') {
          // keep for following char check
          if (key == null && quotedValue == null) {
            throw new Exception("Unexpected '" + charItem + "'-sign at index " + index);
          }
        } else if (key != null) {
          if (charItem == '"' && previousChar != '\\') {
            keyString = key.toString();
            key = null;
          } else if (previousChar == '\\') {
            key.append('\\');
            key.append(charItem);
          } else {
            key.append(charItem);
          }
        } else if (quotedValue != null) {
          if (charItem == '"' && previousChar != '\\') {
            valueObject = quotedValue.toString();
            quotedValue = null;
          } else if (previousChar == '\\') {
            quotedValue.append('\\');
            quotedValue.append(charItem);
          } else {
            quotedValue.append(charItem);
          }
        } else if (Character.isWhitespace(charItem)) {
          // whitespaces outside of keys and values are ignored
        } else if (charItem == ':') {
          // start value for key
          if (keyString == null) {
            throw new Exception("Unexpected ':'-sign at index " + index);
          }
        } else if (charItem == ',' || charItem == '}') {
          // end of value for key or array item
          if (keyString != null && (valueObject != null || unquotedValue != null)) {
            if (unquotedValue != null) {
              valueObject = JsonUtilities.getJsonValue(unquotedValue.toString());
              unquotedValue = null;
            }
            
            if (valueObject == null) {
              addNullValue(keyString);
            } else if (valueObject instanceof JsonObject) {
              add(keyString, (JsonObject) valueObject);
            } else if (valueObject instanceof JsonArray) {
              add(keyString, (JsonArray) valueObject);
            } else if (valueObject instanceof String) {
              add(keyString, (String) valueObject);
            } else if (valueObject instanceof Number) {
              add(keyString, (Number) valueObject);
            } else if (valueObject instanceof Boolean) {
              add(keyString, (Boolean) valueObject);
            }
            
            keyString = null;
            valueObject = null;
          } else if (charItem == ',') {
            throw new Exception("Unexpected '" + charItem + "'-sign at index " + index);
          }
          
          if (charItem == '}') {
            return index;
          }
        } else if (charItem == '{') {
          JsonObject newJsonObject = new JsonObject();
          index = newJsonObject.parse(jsonData, index + 1);
          valueObject = newJsonObject;
        } else if (charItem == '[') {
          JsonArray newJsonArray = new JsonArray();
          index = newJsonArray.parse(jsonData, index + 1);
          valueObject = newJsonArray;
        } else if (charItem == ']') {
          throw new Exception("Unexpected '" + charItem + "'-sign at index " + index);
        } else if (charItem == '"') {
          if (keyString == null) {
            key = new StringBuilder();
          } else {
            quotedValue = new StringBuilder();
          }
        } else {
          if (unquotedValue == null){
            unquotedValue = new StringBuilder();
          }
          unquotedValue.append(charItem);
          if (!JsonUtilities.isValidUnfinishedJsonValue(unquotedValue.toString())){
            throw new Exception("Unexpected '" + charItem + "'-sign at index " + index);
          }
        }
        
        previousChar = charItem;
      }
      
      throw new Exception("Unclosed object value");
    } catch (Exception e) {
      throw new Exception("Invalid JSON data starting at index " + startIndex, e);
    }
  }
}

Dies ist eine Hilfs-Klasse für JsonUtilities

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package de.soderer.utilities.json;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import de.soderer.utilities.TextUtilities;

public class JsonArray implements Iterable<Object> {
  private List<Object> items = new ArrayList<Object>();
  
  public void add(JsonObject object) {
    add((Object) object);
  }
  
  public void add(JsonArray array) {
    add((Object) array);
  }
  
  public void add(String value) {
    add((Object) value);
  }
  
  public void add(Number value) {
    add((Object) value);
  }
  
  public void add(Boolean value) {
    add((Object) value);
  }

  public void addNullValue() {
    add((Object) null);
  }
  
  private void add(Object value) {
    items.add(value);
  }
  
  public Object remove(Object value) {
    return items.remove(value);
  }
  
  public Object get(int index) {
    return items.get(index);
  }
  
  public int size() {
    return items.size();
  }

  @Override
  public Iterator<Object> iterator() {
    return items.iterator();
  }
  
  @Override
  public String toString() {
    return toString("\t", " ", "\n");
  }

  public String toString(String indention, String separator, String linebreak) {
    if (items.size() > 0) {
      boolean isFirstItem = true;
      StringBuilder result = new StringBuilder("[");
      result.append(separator);
      
      for (Object subItem : items) {
        if (!isFirstItem) {
          result.append(",");
          result.append(separator);
        }
        
        if (subItem == null) {
          result.append("null");
        } else if (subItem instanceof JsonObject) {
          result.append(TextUtilities.addLeadingTab(((JsonObject) subItem).toString(indention, separator, linebreak)).trim());
        } else if (subItem instanceof JsonArray) {
          result.append(TextUtilities.addLeadingTab(((JsonArray) subItem).toString(indention, separator, linebreak)).trim());
        } else if (subItem instanceof String) {
          result.append("\"");
          result.append(((String) subItem).replace("\"", "\\\""));
          result.append("\"");
        } else {
          result.append(subItem.toString());
        }
        
        isFirstItem = false;
      }

      result.append(separator);
      result.append("]");
      
      return result.toString();
    } else {
      return "[]";
    }
  }
  
  protected int parse(char[] jsonData, int startIndex) throws Exception {
    try {
      items.clear();
      
      char previousChar = ' ';
      StringBuilder quotedValue = null;
      StringBuilder unquotedValue = null;
      Object valueObject = null;
      
      for (int index = startIndex; index < jsonData.length; index++) {
        char charItem = jsonData[index];
        
        if (charItem == '\\') {
          // keep for following char check
          if (quotedValue == null) {
            throw new Exception("Unexpected '" + charItem + "'-sign at index " + index);
          }
        } else if (quotedValue != null) {
          if (charItem == '"' && previousChar != '\\') {
            valueObject = quotedValue.toString();
            quotedValue = null;
          } else if (previousChar == '\\') {
            quotedValue.append('\\');
            quotedValue.append(charItem);
          } else {
            quotedValue.append(charItem);
          }
        } else if (Character.isWhitespace(charItem)) {
          // whitespaces outside of keys and values are ignored
        } else if (charItem == ':') {
          // start value for key
          throw new Exception("Unexpected ':'-sign at index " + index);
        } else if (charItem == ',' || charItem == ']') {
          // end of value for key or array item
          if (valueObject != null || unquotedValue != null) {
            if (unquotedValue != null) {
              valueObject = JsonUtilities.getJsonValue(unquotedValue.toString());
              unquotedValue = null;
            }
            
            if (valueObject == null) {
              addNullValue();
            } else if (valueObject instanceof JsonObject) {
              add((JsonObject) valueObject);
            } else if (valueObject instanceof JsonArray) {
              add((JsonArray) valueObject);
            } else if (valueObject instanceof String) {
              add((String) valueObject);
            } else if (valueObject instanceof Number) {
              add((Number) valueObject);
            } else if (valueObject instanceof Boolean) {
              add((Boolean) valueObject);
            }
            
            valueObject = null;
          } else if (charItem == ',') {
            throw new Exception("Unexpected '" + charItem + "'-sign at index " + index);
          }
          
          if (charItem == ']') {
            return index;
          }
        } else if (charItem == '[') {
          JsonArray newJsonArray = new JsonArray();
          index = newJsonArray.parse(jsonData, index + 1);
          valueObject = newJsonArray;
        }  else if (charItem == '{') {
          JsonObject newJsonObject = new JsonObject();
          index = newJsonObject.parse(jsonData, index + 1);
          valueObject = newJsonObject;
        } else if (charItem == '}') {
          throw new Exception("Unexpected '" + charItem + "'-sign at index " + index);
        } else if (charItem == '"') {
          quotedValue = new StringBuilder();
        } else {
          if (unquotedValue == null){
            unquotedValue = new StringBuilder();
          }
          unquotedValue.append(charItem);
          if (!JsonUtilities.isValidUnfinishedJsonValue(unquotedValue.toString())){
            throw new Exception("Unexpected '" + charItem + "'-sign at index " + index);
          }
        }
        
        previousChar = charItem;
      }
      
      throw new Exception("Unclosed object value");
    } catch (Exception e) {
      throw new Exception("Invalid JSON data starting at index " + startIndex, e);
    }
  }
}