diff --git a/src/Json/JsonValue.fs b/src/Json/JsonValue.fs index df3ae0cb4..7c12103fd 100644 --- a/src/Json/JsonValue.fs +++ b/src/Json/JsonValue.fs @@ -167,18 +167,18 @@ type private JsonParser(jsonText:string) = if not cond then throw() // Recursive descent parser for JSON that uses global mutable index - let rec parseValue() = + let rec parseValue cont = skipWhitespace() ensure(i < s.Length) match s.[i] with - | '"' -> JsonValue.String(parseString()) - | '-' -> parseNum() - | c when Char.IsDigit(c) -> parseNum() - | '{' -> parseObject() - | '[' -> parseArray() - | 't' -> parseLiteral("true", JsonValue.Boolean true) - | 'f' -> parseLiteral("false", JsonValue.Boolean false) - | 'n' -> parseLiteral("null", JsonValue.Null) + | '"' -> JsonValue.String(parseString()) |> cont + | '-' -> parseNum() |> cont + | c when Char.IsDigit(c) -> parseNum() |> cont + | '{' -> parseObject cont + | '[' -> parseArray cont + | 't' -> parseLiteral("true", JsonValue.Boolean true) |> cont + | 'f' -> parseLiteral("false", JsonValue.Boolean false) |> cont + | 'n' -> parseLiteral("null", JsonValue.Null) |> cont | _ -> throw() and parseString() = @@ -243,47 +243,67 @@ type private JsonParser(jsonText:string) = | Some x -> JsonValue.Float x | _ -> throw() - and parsePair() = + and parsePair cont = let key = parseString() skipWhitespace() ensure(i < s.Length && s.[i] = ':') i <- i + 1 skipWhitespace() - key, parseValue() + parseValue (fun v -> (key, v) |> cont) - and parseObject() = + and parseObject cont = ensure(i < s.Length && s.[i] = '{') i <- i + 1 skipWhitespace() let pairs = ResizeArray<_>() + let parseObjectEnd() = + ensure(i < s.Length && s.[i] = '}') + i <- i + 1 + pairs.ToArray() |> JsonValue.Record |> cont if i < s.Length && s.[i] = '"' then - pairs.Add(parsePair()) - skipWhitespace() - while i < s.Length && s.[i] = ',' do - i <- i + 1 - skipWhitespace() - pairs.Add(parsePair()) + parsePair (fun p -> + pairs.Add(p) skipWhitespace() - ensure(i < s.Length && s.[i] = '}') - i <- i + 1 - JsonValue.Record(pairs.ToArray()) - - and parseArray() = + let rec parsePairItem() = + if i < s.Length && s.[i] = ',' then + i <- i + 1 + skipWhitespace() + parsePair (fun p -> + pairs.Add(p) + skipWhitespace() + parsePairItem()) + else + parseObjectEnd() + parsePairItem()) + else + parseObjectEnd() + + and parseArray cont = ensure(i < s.Length && s.[i] = '[') i <- i + 1 skipWhitespace() let vals = ResizeArray<_>() + let parseArrayEnd() = + ensure(i < s.Length && s.[i] = ']') + i <- i + 1 + vals.ToArray() |> JsonValue.Array |> cont if i < s.Length && s.[i] <> ']' then - vals.Add(parseValue()) - skipWhitespace() - while i < s.Length && s.[i] = ',' do - i <- i + 1 + parseValue (fun v -> + vals.Add(v) skipWhitespace() - vals.Add(parseValue()) - skipWhitespace() - ensure(i < s.Length && s.[i] = ']') - i <- i + 1 - JsonValue.Array(vals.ToArray()) + let rec parseArrayItem() = + if i < s.Length && s.[i] = ',' then + i <- i + 1 + skipWhitespace() + parseValue (fun v -> + vals.Add(v) + skipWhitespace() + parseArrayItem()) + else + parseArrayEnd() + parseArrayItem()) + else + parseArrayEnd() and parseLiteral(expected, r) = ensure(i+expected.Length <= s.Length) @@ -294,7 +314,7 @@ type private JsonParser(jsonText:string) = // Start by parsing the top-level value member x.Parse() = - let value = parseValue() + let value = parseValue id skipWhitespace() if i <> s.Length then throw() @@ -303,7 +323,7 @@ type private JsonParser(jsonText:string) = member x.ParseMultiple() = seq { while i <> s.Length do - yield parseValue() + yield parseValue id skipWhitespace() } diff --git a/tests/FSharp.Data.Tests/JsonValue.fs b/tests/FSharp.Data.Tests/JsonValue.fs index bcb82aa17..1c47128be 100644 --- a/tests/FSharp.Data.Tests/JsonValue.fs +++ b/tests/FSharp.Data.Tests/JsonValue.fs @@ -95,6 +95,18 @@ let ``Can parse document with datetime offset from iso date format``() = let j = JsonValue.Parse "{\"anniversary\": \"2009-05-19 14:39:22+0600\"}" j?anniversary.AsDateTimeOffset() |> should equal <| DateTimeOffset(2009, 05, 19, 14, 39, 22, TimeSpan.FromHours 6.) +[] +let ``Can parse deep arrays``() = + String.replicate 50000 "[" + String.replicate 50000 "]" + |> FSharp.Data.JsonValue.Parse + |> ignore + +[] +let ``Can parse deep objects``() = + (String.replicate 50000 "{\"a\":") + "\"\"" + (String.replicate 50000 "}") + |> FSharp.Data.JsonValue.Parse + |> ignore + // TODO: Due to limitations in the current ISO 8601 datetime parsing these fail, and should be made to pass //[] //let ``Cant Yet parse document with basic iso date``() =