Skip to content
Open
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

A Julia package for reading and writing JSON data.

[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://juliaio.github.io/JSON.jl/stable)
[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://juliaio.github.io/JSON.jl/dev)
[![Build Status](https://github.com/JuliaIO/JSON.jl/workflows/CI/badge.svg)](https://github.com/JuliaIO/JSON.jl/actions/workflows/CI.yml?query=branch%3Amaster)
[![codecov.io](http://codecov.io/github/JuliaIO/JSON.jl/coverage.svg?branch=master)](http://codecov.io/github/JuliaIO/JSON.jl?branch=master)

Expand Down
12 changes: 11 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
using Documenter, JSON

makedocs(modules = [JSON], sitename = "JSON.jl")
makedocs(
modules = [JSON],
sitename = "JSON.jl",
pages = [
"Home" => "index.md",
"JSON Writing" => "writing.md",
"JSON Reading" => "reading.md",
"Migration Guides" => "migrate.md",
"API Reference" => "reference.md",
],
)

deploydocs(repo = "github.com/JuliaIO/JSON.jl.git", push_preview = true)
12 changes: 6 additions & 6 deletions docs/src/migrate.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Migration guides
# Migration Guides

This guide provides an overview of how to migrate your code from either the pre-1.0 JSON.jl package to the 1.0 release or from JSON3.jl. The 1.0 release introduces several improvements and changes, particularly in how JSON is read and written, leveraging StructUtils.jl for customization and extensibility. Below, we outline the key differences and provide step-by-step instructions for updating your code.

Expand All @@ -7,7 +7,7 @@ This guide provides an overview of how to migrate your code from either the pre-
## Migration guide from pre-1.0 -> 1.0

### Writing JSON
- `JSON.json`
- [`JSON.json`](@ref)
- What stayed the same:
- Produces a compact String by default
- Can automatically serialize basic structs in a sensible way
Expand All @@ -33,7 +33,7 @@ This guide provides an overview of how to migrate your code from either the pre-
- Utilizing multiple dispatch to combine `JSON.print` and `JSON.json` and provide convenience for writing to files
- Most opened issues over the last few years were about providing more controls around writing JSON without having to completely implement a custom serializer
- More consistency with `JSON.parse` keyword args with `allownan` and `jsonlines`
- `JSON.print`
- [`JSON.print`](@ref)
- What stayed the same:
- Technically still defined for backwards compatibility, but just calls `JSON.json` under the hood
- Why the changes:
Expand All @@ -55,12 +55,12 @@ This guide provides an overview of how to migrate your code from either the pre-
- There was often confusion about whether a custom Serialization or StructuralContext was needed and what intefaces were then required to implement
- The need to customize separators, delimiters, and indentation, while powerful, can be accomplished much simpler via keyword arguments or is not necessary at all (i.e. JSON.jl shouldn't be too concerned with how to produce anything that isn't JSON)
- Instead of overloading show_string/show_element/show_key/show_pair/show_json, `lower` can be used to accomplish any requirements of "overloading" how values are serialized; the addition of "styles" also allows customizing for non-owned types instead of needing a custom context + `show_json` method
- `JSONText`
- [`JSONText`](@ref)
- What changed:
- Nothing; `JSONText` can still be used to have a JSON-formatted string be written as-is when serializing

### Reading JSON
- `JSON.parse` / `JSON.parsefile`
- [`JSON.parse`](@ref) / [`JSON.parsefile`](@ref)
- What stayed the same:
- These functions take the same JSON input arguments (String, IO, or filename for `parsefile`)
- The `dicttype`, `allownan`, and `null` keyword arguments all remain and implement the same functionality
Expand All @@ -75,7 +75,7 @@ This guide provides an overview of how to migrate your code from either the pre-
- The `inttype` keyword argument is rare among other JSON libraries and doesn't serve a strong purpose; memory gains from possibly using smaller ints is minimal and leads to more error-prone code via overflows by trying to force integers into non-standard small types
- For the `allownan` default value change, there are many benchmarks/JSON-accuracy checking test suites that enforce adherance to the specification; following the specification by default is recommended and common across language JSON libraries
- Mmapping is an internal detail that most users shouldn't worry about anyway, and it can be done transparently without any outside affect to the user
- `JSONText`
- [`JSONText`](@ref)
- `JSONText` can now also be used while parsing, as a field type of a struct or directly to return the raw JSON (similar to how writing with `JSONText` works)

## Migration guide for JSON3.jl
Expand Down
31 changes: 14 additions & 17 deletions docs/src/reading.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,39 @@ This guide to reading JSON in the JSON.jl package aims to:
- Provide a comprehensive overview of the JSON reading process.
- Explain the various options and configurations available for reading JSON data.
- Offer practical examples to illustrate the usage of different functions and options.

```@contents
```

## Core JSON Parsing - `JSON.lazy` and `JSON.LazyValue`

There are several "entrypoints" to reading JSON in JSON.jl, including:
- `JSON.parse`/`JSON.parse!`
- `JSON.parsefile`/`JSON.parsefile!`
- `JSON.lazy`/`JSON.lazyfile`
- `JSON.isvalidjson`
- [`JSON.parse`](@ref)/`JSON.parse!`
- [`JSON.parsefile`](@ref)/[`JSON.parsefile!`](@ref)
- [`JSON.lazy`](@ref)/[`JSON.lazyfile`](@ref)
- [`JSON.isvalidjson`](@ref)

These functions are all built to accept the same kinds of JSON inputs:

| Accepted `json` sources | Notes |
|--------------------------------------------|---------------------------------------------------|
|:-------------------------------------------|:--------------------------------------------------|
| `AbstractString` | UTF‑8; UTF‑8‑BOM handled automatically |
| `AbstractVector{UInt8}` | zero‑copy if already bytes |
| `IO`, `IOStream`, `Base.AbstractCmd` | stream fully read into a byte vector |

The core JSON parsing machinery is hence built around having an `AbstractVector{UInt8}` or `AbstractString` JSON input where individual bytes can be parsed to identify JSON structure, validate syntax, and ultimately produce Julia-level values.

Each entrypoint function first calls `JSON.lazy`, which will consume the JSON input until the type of the next JSON value can be identified (`{` for objects, `[` for arrays, `"` for strings, `t` for true, `f` for false, `n` for null, and `-` or a digit for numbers). `JSON.lazy` returns a `JSON.LazyValue`, which wraps the JSON input buffer (`AbstractVector{UInt8}` or `AbstractString`), and marks the byte position the value starts at, the type of the value, and any keyword arguments that were provided that may affect parsing. Currently supported parsing-specific keyword arguments to `JSON.lazy` (and thus all other entrypoint functions) include:
Each entrypoint function first calls [`JSON.lazy`](@ref), which will consume the JSON input until the type of the next JSON value can be identified (`{` for objects, `[` for arrays, `"` for strings, `t` for true, `f` for false, `n` for null, and `-` or a digit for numbers). [`JSON.lazy`](@ref) returns a [`JSON.LazyValue`](@ref), which wraps the JSON input buffer (`AbstractVector{UInt8}` or `AbstractString`), and marks the byte position the value starts at, the type of the value, and any keyword arguments that were provided that may affect parsing. Currently supported parsing-specific keyword arguments to [`JSON.lazy`](@ref) (and thus all other entrypoint functions) include:

- `allownan::Bool = false`: whether "special" float values shoudl be allowed while parsing (`NaN`, `Inf`, `-Inf`); these values are specifically _not allowed_ in the JSON spec, but many JSON libraries allow reading/writing
- `ninf::String = "-Infinity"`: the string that will be used to parse `-Inf` if `allownan=true`
- `inf::String = "Infinity"`: the string that will be used to parse `Inf` if `allownan=true`
- `nan::String = "NaN"`: the string that will be sued to parse `NaN` if `allownan=true`
- `jsonlines::Bool = false`: whether the JSON input should be treated as an implicit array, with newlines separating individual JSON elements with no leading `'['` or trailing `']'` characters. Common in logging or streaming workflows. Defaults to `true` when used with `JSON.parsefile` and the filename extension is `.jsonl` or `ndjson`. Note this ensures that parsing will _always_ return an array at the root-level.
- `jsonlines::Bool = false`: whether the JSON input should be treated as an implicit array, with newlines separating individual JSON elements with no leading `'['` or trailing `']'` characters. Common in logging or streaming workflows. Defaults to `true` when used with [`JSON.parsefile`](@ref) and the filename extension is `.jsonl` or `ndjson`. Note this ensures that parsing will _always_ return an array at the root-level.
- Materialization-specific keyword arguments (i.e. they affect materialization, but not parsing)
- `dicttype = JSON.Object{String, Any}`: type to parse JSON objects as by default (recursively)
- `null = nothing`: value to return for JSON `null` value

So what can we do with a `JSON.LazyValue`?
So what can we do with a [`JSON.LazyValue`](@ref)?

```julia
```julia-repl
julia> x = JSON.lazy("{\"a\": 1, \"b\": null, \"c\": true, \"d\": false, \"e\": \"\", \"f\": [1,2,3], \"g\": {\"h\":{\"i\":\"foo\"}}}")
LazyObject{String} with 7 entries:
"a" => JSON.LazyValue(1)
Expand All @@ -55,7 +52,7 @@ Note that for convenience at the REPL, special `show` overloads enable displayin
`LazyValue`s support convenient syntax for both _navigating_ their structure and _materializing_, with an aim
to support lazy workflows. Examples include:

```julia
```julia-repl
# convenient "get" syntax on lazy objects
julia> x.a
JSON.LazyValue(1)
Expand Down Expand Up @@ -115,7 +112,7 @@ Ok, but at some point, we _do_ actually need Julia values to operate on, so let'

In the `LazyValue` syntax example, it was shown that empty `getindex` will result in a "default" materialization of a `LazyValue`:

```julia
```julia-repl
julia> x[]
JSON.Object{String, Any} with 7 entries:
"a" => 1
Expand All @@ -127,10 +124,10 @@ JSON.Object{String, Any} with 7 entries:
"g" => Object{String, Any}("h"=>Object{String, Any}("i"=>"foo"))
```

Under the hood, this `getindex` call is really calling `JSON.parse(lazyvalue)`. `JSON.parse` can also be called as a main entrypoint function with all the same input types as `JSON.lazy`. This form of `parse` is referred to as "untyped parsing" or "untyped materialization". It allocates and _materializes_ the raw JSON values into appropriate "default" Julia-level values. In particular:
Under the hood, this `getindex` call is really calling `JSON.parse(lazyvalue)`. [`JSON.parse`](@ref) can also be called as a main entrypoint function with all the same input types as [`JSON.lazy`](@ref). This form of `parse` is referred to as "untyped parsing" or "untyped materialization". It allocates and _materializes_ the raw JSON values into appropriate "default" Julia-level values. In particular:

| JSON construct | Default Julia value |
|----------------|---------------------------------------------------------------------------|
|:---------------|:--------------------------------------------------------------------------|
| object | `JSON.Object{String,Any}` (order‑preserving drop-in replacement for Dict) |
| array | `Vector{Any}` |
| string | `String` |
Expand All @@ -145,7 +142,7 @@ Because `Object` uses a linked-list implementation, key lookups are `O(n)`, perf

## `JSON.parse` - Typed materialization

While untyped materialization is convenient for quick exploration, one of the most powerful features of JSON.jl is its ability to directly parse JSON into concrete Julia types. This is done by providing a type as the second argument to `JSON.parse` and opens up a world of type-safe JSON parsing with minimal boilerplate.
While untyped materialization is convenient for quick exploration, one of the most powerful features of JSON.jl is its ability to directly parse JSON into concrete Julia types. This is done by providing a type as the second argument to [`JSON.parse`](@ref) and opens up a world of type-safe JSON parsing with minimal boilerplate.

### Basic usage with structs

Expand Down
3 changes: 0 additions & 3 deletions docs/src/reference.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# API Reference

```@contents
```

```@autodocs
Modules = [JSON]
```
17 changes: 7 additions & 10 deletions docs/src/writing.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@ This guide to writing JSON in the JSON.jl package aims to:
- Explain the various options and configurations available for writing JSON data.
- Offer practical examples to illustrate the usage of different functions and options.

```@contents
```

## Core JSON Serialization - `JSON.json`

The main entrypoint for serializing Julia values to JSON in JSON.jl is the `JSON.json` function. This function offers flexible output options:
The main entrypoint for serializing Julia values to JSON in JSON.jl is the [`JSON.json`](@ref) function. This function offers flexible output options:

```julia
# Serialize to a String
Expand All @@ -23,10 +20,10 @@ JSON.json(io::IO, x) -> IO
JSON.json(file_name::String, x) -> String
```

The `JSON.json` function accepts a wide range of Julia types and transforms them into their JSON representation by knowing how to serialize a core set of types:
The [`JSON.json`](@ref) function accepts a wide range of Julia types and transforms them into their JSON representation by knowing how to serialize a core set of types:

| Julia type | JSON representation |
|------------------------------------|------------------------------------------|
|:-----------------------------------|:------------------------------------------|
| `Nothing` | `null` |
| `Bool` | `true` or `false` |
| `Number` | Numeric value (integer or floating point) |
Expand All @@ -40,11 +37,11 @@ For values that don't fall into one of the above categories, `JSON.lower` will b

## Customizing JSON Output

`JSON.json` supports numerous keyword arguments to control how data is serialized:
[`JSON.json`](@ref) supports numerous keyword arguments to control how data is serialized:

### Pretty Printing

By default, `JSON.json` produces compact JSON without extra whitespace. For human-readable output:
By default, [`JSON.json`](@ref) produces compact JSON without extra whitespace. For human-readable output:

```julia
# Boolean flag for default pretty printing (2-space indent)
Expand Down Expand Up @@ -355,7 +352,7 @@ JSON.json(config)

## Handling Circular References

`JSON.json` automatically detects circular references to prevent infinite recursion:
[`JSON.json`](@ref) automatically detects circular references to prevent infinite recursion:

```julia
mutable struct Node
Expand All @@ -374,7 +371,7 @@ JSON.json(node; omit_null=false)

## Custom Dictionary Key Serialization

For dictionaries with non-string keys, `JSON.json` has a few default `lowerkey` definitions to convert keys to strings:
For dictionaries with non-string keys, [`JSON.json`](@ref) has a few default `lowerkey` definitions to convert keys to strings:

```julia
# Integer keys
Expand Down
Loading