public static DataNode ReadFromString(string contents) { var root = DataNode.CreateArray(null); //Console.WriteLine(contents); var header = new List <string>(); int index = 0; var state = State.Header; char c; int fieldIndex = 0; bool isEscaped = false; var content = new StringBuilder(); DataNode currentNode = null; while (index < contents.Length) { c = contents[index]; index++; switch (state) { case State.Header: { if (c == ',' || c == '\n') { header.Add(content.ToString().Trim()); content.Length = 0; } switch (c) { case ',': { break; } case '\n': { state = State.Content; break; } default: { content.Append(c); break; } } break; } case State.Content: { if (!isEscaped && (c == ',' || c == '\n')) { if (fieldIndex < header.Count) { currentNode.AddField(header[fieldIndex], content.ToString()); } content.Length = 0; fieldIndex++; if (c == '\n') { fieldIndex = 0; currentNode = null; } break; } if (c == '"') { if (isEscaped && index < contents.Length && contents[index] == '"') { index++; } else { isEscaped = !isEscaped; break; } } if (currentNode == null) { currentNode = DataNode.CreateObject(null); root.AddNode(currentNode); } content.Append(c); break; } } } if (currentNode != null && fieldIndex < header.Count) { currentNode.AddField(header[fieldIndex], content.ToString()); } return(root); }
private static DataNode ReadNode(string contents, ref int index, string name) { DataNode result = null; var state = State.Type; char c; var mode = InputMode.None; StringBuilder name_content = new StringBuilder(); StringBuilder value_content = new StringBuilder(); int rewind_index = index; bool is_escaped = false; do { bool isWhiteSpace; bool next = false; do { if (index >= contents.Length) { if (state == State.Next) { return(result); } throw new Exception($"JSON parsing exception, unexpected end of data"); } c = contents[index]; isWhiteSpace = Char.IsWhiteSpace(c); if (!isWhiteSpace) { rewind_index = index; } index++; next = (mode == InputMode.None) ? isWhiteSpace : false; } while (next); switch (state) { case State.Type: { switch (c) { case '{': { result = DataNode.CreateObject(name); state = State.Name; break; } case '[': { result = DataNode.CreateArray(name); state = State.Value; break; } default: { throw new Exception($"JSON parsing exception at, unexpected character"); //throw new Exception($"JSON parsing exception at {ParserUtils.GetOffsetError(contents, index)}, unexpected character"); } } break; } case State.Name: { if (c == '}' && result.Kind == NodeKind.Object) { return(result); } switch (c) { case '"': { if (mode == InputMode.None) { mode = InputMode.Text; name_content.Length = 0; } else { mode = InputMode.None; state = State.Colon; } break; } default: { if (mode == InputMode.Text) { name_content.Append(c); } else { throw new Exception($"JSON parsing exception at, unexpected character"); //throw new Exception($"JSON parsing exception at {ParserUtils.GetOffsetError(contents, index)}, unexpected character"); } break; } } break; } case State.Colon: { switch (c) { case ':': { state = State.Value; break; } default: { throw new Exception($"JSON parsing exception at, expected collon"); //throw new Exception($"JSON parsing exception at {ParserUtils.GetOffsetError(contents, index)}, expected collon"); } } break; } case State.Value: { if (c == '\\' && !is_escaped) { is_escaped = true; } else if (is_escaped) { is_escaped = false; if (c == 'n') // Newline { value_content.Append('\n'); } else if (c == 'r') // Carriage return { value_content.Append('\r'); } else if (c == 't') // Tab { value_content.Append('\t'); } else if (c == 'b') // Backspace { value_content.Append('\b'); } else if (c == 'f') // Form feed { value_content.Append('\f'); } else { if (c == 'u') { var hex = ""; for (int i = 0; i < 4; i++) { if (index >= contents.Length) { throw new Exception($"JSON parsing exception, unexpected end of data"); } hex += contents[index]; index++; } ushort unicode_val; unicode_val = ushort.Parse(hex, System.Globalization.NumberStyles.HexNumber); c = (char)unicode_val; } value_content.Append(c); } } else if (c == 'n' && mode == InputMode.None) { ReadString("null", contents, ref index); result.AddField(name_content.Length == 0 ? null : name_content.ToString(), null); state = State.Next; } else if (c == 'f' && mode == InputMode.None) { ReadString("false", contents, ref index); result.AddField(name_content.Length == 0 ? null : name_content.ToString(), false); state = State.Next; } else if (c == 't' && mode == InputMode.None) { ReadString("true", contents, ref index); result.AddField(name_content.Length == 0 ? null : name_content.ToString(), true); state = State.Next; } else if (c == ']' && mode == InputMode.None && result.Kind == NodeKind.Array) { return(result); } else { switch (c) { case '"': { if (mode == InputMode.None) { mode = InputMode.Text; value_content.Length = 0; } else { object value; var str = value_content.ToString(); if (mode == InputMode.Number) { if (str.Contains("e")) { // TODO } value = str; } else { value = str; } mode = InputMode.None; result.AddField(name_content.Length == 0 ? null : name_content.ToString(), value); state = State.Next; } break; } case '[': case '{': { if (mode == InputMode.Text) { value_content.Append(c); } else { index = rewind_index; var node = ReadNode(contents, ref index, name_content.Length == 0 ? null : name_content.ToString()); result.AddNode(node); state = State.Next; } break; } default: { if (mode == InputMode.Text) { value_content.Append(c); } else if (char.IsNumber(c) || (c == '.' || c == 'e' || c == 'E' || c == '-' || c == '+')) { if (mode != InputMode.Number) { value_content.Length = 0; mode = InputMode.Number; } if (c == 'E') { c = 'e'; } value_content.Append(c); } else { if (mode == InputMode.Number) { mode = InputMode.None; var numStr = value_content.ToString(); if (numStr.Contains("e")) { var num = double.Parse(numStr, NumberStyles.Any, CultureInfo.InvariantCulture); result.AddField(name_content.Length == 0 ? null : name_content.ToString(), num); } else { var num = decimal.Parse(numStr, NumberStyles.Any, CultureInfo.InvariantCulture); result.AddField(name_content.Length == 0 ? null : name_content.ToString(), num); } state = State.Next; if (c == ',' || c == ']' || c == '}') { index = rewind_index; } } else { //throw new Exception($"JSON parsing exception at {ParserUtils.GetOffsetError(contents, index)}, unexpected character"); } } break; } } } break; } case State.Next: { switch (c) { case ',': { state = result.Kind == NodeKind.Array ? State.Value : State.Name; break; } case '}': { if (result.Kind != NodeKind.Object) { //throw new Exception($"JSON parsing exception at {ParserUtils.GetOffsetError(contents, index)}, unexpected }}"); } return(result); } case ']': { if (result.Kind != NodeKind.Array) { //throw new Exception($"JSON parsing exception at {ParserUtils.GetOffsetError(contents, index)}, unexpected ]"); } return(result); } default: { throw new Exception($"JSON parsing exception at, expected collon"); //throw new Exception($"JSON parsing exception at {ParserUtils.GetOffsetError(contents, index)}, expected collon"); } } break; } } } while (true); }
private static DataNode ReadNode(string contents, ref int index) { DataNode result = null; var state = State.Next; var prevState = State.Next; char c; StringBuilder name_content = new StringBuilder(); StringBuilder value_content = new StringBuilder(); int rewind_index = index; do { bool isWhiteSpace; bool next = false; bool inside = state == State.Content || state == State.TagOpen || state == State.TagClose || state == State.AttributeName || state == State.AttributeValue || state == State.CData; do { if (index >= contents.Length) { if (state == State.Next) // no useful data { return(null); } throw new Exception($"XML parsing exception, unexpected end of data"); } c = contents[index]; isWhiteSpace = Char.IsWhiteSpace(c); if (!isWhiteSpace) { rewind_index = index; } index++; next = isWhiteSpace && !inside; } while (next); switch (state) { case State.Next: { switch (c) { case '<': { state = State.TagOpen; name_content.Length = 0; break; } default: { throw new Exception($"XML parsingexception at unexpected character"); // throw new Exception($"XML parsingexception at {ParserUtils.GetOffsetError(contents, index)}, unexpected character"); } } break; } case State.TagOpen: { switch (c) { case '?': { if (contents[index - 2] == '<') { state = State.Prolog; } else { name_content.Append(c); } break; } case '!': { if (index < contents.Length - 3 && contents[index - 2] == '<' && contents[index] == '-' && contents[index + 1] == '-') { state = State.Comment; prevState = State.Next; } else { name_content.Append(c); } break; } case '/': { result = DataNode.CreateObject(name_content.ToString()); state = State.TagClose; break; } case '>': { result = DataNode.CreateObject(name_content.ToString()); state = State.Content; break; } case ' ': { result = DataNode.CreateObject(name_content.ToString()); name_content.Length = 0; state = State.AttributeName; break; } default: { name_content.Append(c); break; } } break; } case State.TagClose: { switch (c) { case '>': { // previously created: // result = DataNode.CreateObject(name_content.ToString()); return(result); } default: { // TODO: verify that the close tag matches the open tag // name_content.Append(c); break; } } break; } case State.AttributeName: { switch (c) { case '/': { state = State.TagClose; break; } case '=': { state = State.AttributeQuote; break; } default: { if (name_content.Length > 0 || !char.IsWhiteSpace(c)) { name_content.Append(c); } break; } } break; } case State.AttributeQuote: { if (c == '"') { state = State.AttributeValue; value_content.Length = 0; } else { //throw new Exception($"XML parsingexception at {ParserUtils.GetOffsetError(contents, index)}, unexpected character"); } break; } case State.AttributeValue: { switch (c) { case '"': { result.AddField(name_content.ToString(), value_content.ToString()); value_content.Length = 0; state = State.NextAttribute; break; } default: { value_content.Append(c); break; } } break; } case State.NextAttribute: { switch (c) { case '/': { break; } case '>': { if (contents[index - 2] == '/') { return(result); } state = State.Content; break; } default: { if (char.IsLetter(c)) { name_content.Length = 0; name_content.Append(c); state = State.AttributeName; } else { //throw new Exception($"XML parsingexception at {ParserUtils.GetOffsetError(contents, index)}, unexpected character"); } break; } } break; } case State.Prolog: { if (c == '>') { state = State.Next; } break; } case State.Comment: { switch (c) { case '>': { if (contents[index - 2] == '-' && contents[index - 3] == '-') { state = prevState; } break; } } break; } case State.CData: { if (c == ']' && contents[index - 2] == c && index < contents.Length && contents[index] == '>') { state = State.Content; value_content.Length--; index++; } else { value_content.Append(c); } break; } case State.Content: { switch (c) { case '<': { if (CDataAt(contents, index)) { state = State.CData; index += 8; } else if (index < contents.Length && contents[index] == '/') { state = State.TagClose; result.Value += value_content.ToString(); } else if (index < contents.Length - 3 && contents[index] == '!' && contents[index + 1] == '-' && contents[index + 2] == '-') { state = State.Comment; prevState = State.Content; index += 2; } else { index--; var child = ReadNode(contents, ref index); if (child == null) // only valid at top-level. Here must be an input error { throw new Exception("XML parsing exception, unexpected end of data"); } result.AddNode(child); } break; } default: { value_content.Append(c); break; } } break; } } } while (true); }