Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 54 additions & 34 deletions src/Json/JsonValue.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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() =
Expand Down Expand Up @@ -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)
Expand All @@ -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()
Expand All @@ -303,7 +323,7 @@ type private JsonParser(jsonText:string) =
member x.ParseMultiple() =
seq {
while i <> s.Length do
yield parseValue()
yield parseValue id
skipWhitespace()
}

Expand Down
12 changes: 12 additions & 0 deletions tests/FSharp.Data.Tests/JsonValue.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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.)

[<Test; Explicit>]
let ``Can parse deep arrays``() =
String.replicate 50000 "[" + String.replicate 50000 "]"
|> FSharp.Data.JsonValue.Parse
|> ignore

[<Test; Explicit>]
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
//[<Test>]
//let ``Cant Yet parse document with basic iso date``() =
Expand Down