private static void SetError(int lineIndex, int charIndex, string message, out JsonException exception) { exception = new JsonException(message, lineIndex, charIndex); }
public static JsonObject Parse(string text, out JsonException exception) { exception = null; if (!text.TrimStart().StartsWith("{")) { exception = new JsonException("JsonNode should start with {", 0, 0); return null; } Stack<string> path = new Stack<string>(); Stack<JsonObject> nodeStack = new Stack<JsonObject>(); JsonObject currentNode = null; bool inQuats = false; bool isEscaped = false; string key = null; StringBuilder stringBuilder = new StringBuilder(); int textLineIndex = 0; int textCharIndex = 0; int keyLineIndex = 0; int keyCharIndex = 0; int seperatorLineIndex = 0; int seperatorCharIndex = 0; int charIndex = 0; int lineIndex = 0; bool needSeperator = false; StringBuilder lineBuilder = new StringBuilder(); for (int index = 0; index < text.Length; index++) { char c = text[index]; lineBuilder.Append(c); charIndex++; if (lineBuilder.Length > 1 & (c == '\n' || c == '\r')) { lineIndex++; lineBuilder.Remove(0, lineBuilder.Length); charIndex = 0; } if (!inQuats && Char.IsWhiteSpace(c)) { continue; } if (inQuats && (c != '"' || isEscaped)) { if (stringBuilder.Length == 0) { textLineIndex = lineIndex; textCharIndex = charIndex; } if (c == '\\' && !isEscaped) { isEscaped = true; continue; } if (isEscaped) { switch (c) { case 't': stringBuilder.Append('\t'); break; case '"': stringBuilder.Append('"'); break; case '\'': stringBuilder.Append('\''); break; case 'r': stringBuilder.Append('\r'); break; case 'n': stringBuilder.Append('\n'); break; case '\\': stringBuilder.Append('\\'); break; default: exception = new JsonException("unknown escape charctar \\" + c, lineIndex, charIndex); return null; } isEscaped = false; continue; } stringBuilder.Append(c); continue; } switch (c) { case '{': { if (needSeperator) { SetError(seperatorLineIndex, seperatorCharIndex, "expected comma.", out exception); while (nodeStack.Count > 0) currentNode = nodeStack.Pop(); return currentNode; } needSeperator = false; if (key == null && currentNode == null) { currentNode = new JsonNode(lineIndex, charIndex); nodeStack.Push(currentNode); break; } JsonNode newJsonNode = new JsonNode(lineIndex, charIndex); JsonNode parentJsonNode = currentNode as JsonNode; if (parentJsonNode != null) { path.Push(key); parentJsonNode.Add(key, newJsonNode, lineIndex, charIndex); key = null; } else { if (!(currentNode is JsonArray)) throw new JsonException("Exspected JsonArray", lineIndex, charIndex); path.Push("[" + DeterminArrayIndex(text, index) + "]"); ((JsonArray)currentNode).Add(newJsonNode); } nodeStack.Push(currentNode); currentNode = newJsonNode; } break; case '}': { needSeperator = true; seperatorCharIndex = charIndex + 1; seperatorLineIndex = lineIndex; path.Push(key); CheckForNumboerAndBool(stringBuilder, currentNode, ref key, textLineIndex, textCharIndex, keyLineIndex, keyCharIndex); path.Pop(); if (currentNode is JsonArray) { SetError(lineIndex, charIndex, "] expected first.", out exception); while (nodeStack.Count > 0) currentNode = nodeStack.Pop(); return currentNode; } if (nodeStack.Count == 0) { SetError(lineIndex, charIndex, "To many }", out exception); while (nodeStack.Count > 0) currentNode = nodeStack.Pop(); return currentNode; } if (path.Count > 0) path.Pop(); currentNode = nodeStack.Pop(); } break; case '[': { if (needSeperator) { SetError(seperatorLineIndex, seperatorCharIndex, "expected comma.", out exception); while (nodeStack.Count > 0) currentNode = nodeStack.Pop(); return currentNode; } needSeperator = false; if (key == null && currentNode == null) { currentNode = new JsonArray(lineIndex, charIndex); nodeStack.Push(currentNode); break; } JsonArray newJsonArray = new JsonArray(lineIndex, charIndex); JsonNode parentJsonNode = currentNode as JsonNode; if (parentJsonNode != null) { path.Push(key); parentJsonNode.Add(key, newJsonArray, lineIndex, charIndex); key = null; } else { if (!(currentNode is JsonArray)) throw new JsonException("Exspected JsonArray", lineIndex, charIndex); path.Push("[" + DeterminArrayIndex(text, index) + "]"); ((JsonArray)currentNode).Add(newJsonArray); } nodeStack.Push(currentNode); currentNode = newJsonArray; } break; case ']': { needSeperator = true; seperatorCharIndex = charIndex + 1; seperatorLineIndex = lineIndex; path.Push(key); CheckForNumboerAndBool(stringBuilder, currentNode, ref key, lineIndex, charIndex, keyLineIndex, keyCharIndex); path.Pop(); if (currentNode is JsonNode) { SetError(lineIndex, charIndex, "} expected first.", out exception); return null; } if (nodeStack.Count == 0) { SetError(lineIndex, charIndex, "To many ]", out exception); return null; } path.Pop(); currentNode = nodeStack.Pop(); } break; case ':': if (key == null) { SetError(keyLineIndex, keyCharIndex, "value missing", out exception); while (nodeStack.Count > 0) currentNode = nodeStack.Pop(); return currentNode; } break; case '"': if (isEscaped) { stringBuilder.Append("\""); isEscaped = false; break; } if (needSeperator) { SetError(seperatorLineIndex, seperatorCharIndex, "expected comma.", out exception); while (nodeStack.Count > 0) currentNode = nodeStack.Pop(); return currentNode; } if (inQuats) { inQuats = false; JsonNode node = currentNode as JsonNode; if (node != null) { if (key == null) { key = stringBuilder.ToString(); keyLineIndex = lineIndex; keyCharIndex = charIndex; path.Push(key); path.Pop(); } else { path.Push(key); node.Add(key, new JsonValue(stringBuilder.ToString(), textLineIndex, textCharIndex), lineIndex, charIndex); path.Pop(); key = null; needSeperator = true; seperatorCharIndex = charIndex + 1; seperatorLineIndex = lineIndex; } } else { if (!(currentNode is JsonArray)) throw new JsonException("Exspected JsonArray", lineIndex, charIndex); JsonArray jsonArray = currentNode as JsonArray; jsonArray.Add(new JsonValue(stringBuilder.ToString(), textLineIndex, textCharIndex)); } stringBuilder.Remove(0, stringBuilder.Length); } else { if (stringBuilder.Length > 0) { SetError(textLineIndex, textCharIndex, "text in unexpected place", out exception); while (nodeStack.Count > 0) currentNode = nodeStack.Pop(); return currentNode; } inQuats = true; } break; case '\\': if (!inQuats) { SetError(lineIndex, charIndex, "\\ found in wrong place", out exception); while (nodeStack.Count > 0) currentNode = nodeStack.Pop(); return currentNode; } if (isEscaped) { stringBuilder.Append("\\"); isEscaped = false; } else isEscaped = true; break; case ',': { if (key == null && !needSeperator && currentNode is JsonNode) { SetError(lineIndex, charIndex, "extra comma found", out exception); while (nodeStack.Count > 0) currentNode = nodeStack.Pop(); return currentNode; } needSeperator = false; path.Push(key); CheckForNumboerAndBool(stringBuilder, currentNode, ref key, lineIndex, charIndex, keyLineIndex, keyCharIndex); path.Pop(); } break; default: isEscaped = false; if (needSeperator) { SetError(seperatorLineIndex, seperatorCharIndex, "expected comma.", out exception); while (nodeStack.Count > 0) currentNode = nodeStack.Pop(); return currentNode; } if (stringBuilder.Length == 0) { textLineIndex = lineIndex; textCharIndex = charIndex; } stringBuilder.Append(c); break; } } if (stringBuilder.Length > 0) SetError(textLineIndex, textCharIndex, "text in unexpected place", out exception); if (key != null) SetError(keyLineIndex, keyCharIndex, "value missing", out exception); if (nodeStack.Count > 0) { SetError(lineBuilder.Length > 0 ? lineIndex : lineIndex - 1, charIndex, currentNode is JsonArray ? "missing ]" : "missing }", out exception); } while (nodeStack.Count > 0) currentNode = nodeStack.Pop(); return currentNode; }