JSON2.jl

Fast json marshalling/unmarshalling with Julia types
Popularity
33 Stars
Updated Last
1 Month Ago
Started In
September 2017

JSON2

Fast JSON for Julia types

PackageEvaluator Build Status

Installation

The package is registered in METADATA.jl and so can be installed with Pkg.add.

julia> Pkg.add("JSON2")

Project Status

The package is tested against the current Julia v1.0 release and nightly on Linux and OS X.

Contributing and Questions

Contributions are very welcome, as are feature requests and suggestions. Please open an issue if you encounter any problems or would just like to ask a question.

Documentation

For most use-cases, all you ever need are:

JSON2.write(obj) => String
JSON2.read(str, T) => T
@pretty json_string # print a "prettified" version of a JSON string

Native support for reading/writing is provided for:

  • NamedTuple
  • Array
  • Number
  • Nothing/Missing: corresponds to JSON null
  • String
  • Bool
  • JSON2.Function: type that represents a javascipt function (stored in plain text)
  • Union{T, Nothing}
  • AbstractDict
  • Tuple
  • Set
  • Char
  • Symbol
  • Enum
  • Date/DateTime

Custom types are supported by default as well, utilizing reflection to generate compiled JSON parsers for a type's fields. So in general, you really can just do JSON2.read(str, MyType) and everything will "Just Work" (and be freaky fast as well!).

Custom JSON Formatting

Default

In many cases, a type doesn't even need to use JSON2.@format since the default reflection-based parsing is somewhat flexible. By default, the JSON input is expected to contain each field of a type and be in the same order as the type was defined. For example, the struct:

struct T
    a::Int
    b::Int
    c::Union{Nothing, Int}
end

Could have valid JSON in the forms:

{"a": 0, "b": 1, "c": null} // all 3 fields provided in correct order
{"a": 0, "b": 1, "c": 2}
{"a": 0, "b": 1, "c": null, "d": 3} // extra fields are ignored
{"a": 0} // will work if T(a) constructor is defined
{"a": 0, "b": 1} // will work if T(a, b) constructor is defined

That is, each field must be present in the JSON input and match in position to the original struct definition. Extra arguments after the struct's own fieldtypes are ignored. As noted, the exception to a field needing to be present is if 1) the field and all subsequent fields are not present and 2) appropriate constructors are defined that take these limited subsets of inputs when constructing, e.g. T(a), T(a, b), etc.

JSON.@format T

JSON2.@format T [noargs|keywordargs] begin
    _field_ => (; options...)
    _field2_ => (; options...)
end

Specify a custom JSON formatting for a struct T, with individual field options being given like fieldname => (; option1=value1, option2=value2), i.e a Pair of the name of the field to a NamedTuple of options. Valid field options include:

  • name: if a field's name should be read/written differently than it's defined name
  • jsontype: if the JSON type of a field is different than the julia field type, the JSON type can be provided like jsontype=String
  • omitempty: whether an "empty" julia field should still be written; applies to collection types like AbstractArray, AbstractDict, AbstractSet, etc.
  • exclude: whether a julia field should be excluded altogether from JSON reading/writing
  • default: a default value that can be provided for a julia field if it may not appear in a JSON input string when parsing

Again, the default case is for JSON input that will have consistently ordered, always-present fields; for cases where the input JSON is not well-ordered or if there is a possibility of a field not being present in the JSON input, there are a few additional options for custom parsing.

Default field values

If the JSON input fields will always be consistenly-ordered, but fields may be missing (i.e. field isn't present at all in the input), field defaults can be provided like:

JSON2.@format T begin
    c => (default=0,)
end

This says that, when reading from a JSON input, if field c isn't present, to set it's value to 0.

If the JSON input is not consistenly-ordered, there are two other options for allowing direct type parsing

Keywordargs Constructor

T(; a=0, b=0, c=0, kwargs...) = T(a, b, c)
JSON2.@format T keywordargs begin
    # ...
end

Here we've defined a "keywordargs" constructor for T that essentially takes a default for each field as keyword arguments, then constructs T. During parsing, the JSON input will be parsed for any valid field key-values and the keyword constructor will be called with whatever arguments are parsed in whatever order. Note that we also included a catchall kwargs... in our constructor which can be used to "throw away" or ignore any extra fields in the JSON input.

Noargs Constructor

mutable struct T
    a::Int
    b::Int
    c::Union{Nothing, Int}
end
T() = T(0, 0, 0)
JSON2.@format T noargs begin
    #...
end

In this case, we've made T a mutable struct and defined a "noargs" constructor T() = ...; we then specified in JSON2.@format T noargs the noargs option. During parsing, an instance of T will first constructed using the "noargs" constructor, then fields will be set as they're parsed from the JSON input (hence why mutable struct is required).

Required Packages