v/vlib/x/json2
Larsimusrex 24f91280d9
Some checks failed
Graphics CI / gg-regressions (push) Waiting to run
vlib modules CI / build-module-docs (push) Waiting to run
Shy and PV CI / v-compiles-puzzle-vibes (push) Waiting to run
Sanitized CI / sanitize-address-msvc (push) Waiting to run
Sanitized CI / tests-sanitize-address-clang (push) Waiting to run
Sanitized CI / sanitize-undefined-clang (push) Waiting to run
Sanitized CI / sanitize-undefined-gcc (push) Waiting to run
Sanitized CI / sanitize-address-gcc (push) Waiting to run
Sanitized CI / sanitize-memory-clang (push) Waiting to run
sdl CI / v-compiles-sdl-examples (push) Waiting to run
Time CI / time-linux (push) Waiting to run
Time CI / time-macos (push) Waiting to run
Time CI / time-windows (push) Waiting to run
toml CI / toml-module-pass-external-test-suites (push) Waiting to run
Tools CI / tools-linux (clang) (push) Waiting to run
Tools CI / tools-linux (gcc) (push) Waiting to run
Tools CI / tools-linux (tcc) (push) Waiting to run
Tools CI / tools-macos (clang) (push) Waiting to run
Tools CI / tools-windows (gcc) (push) Waiting to run
Tools CI / tools-windows (msvc) (push) Waiting to run
Tools CI / tools-windows (tcc) (push) Waiting to run
Tools CI / tools-docker-ubuntu-musl (push) Waiting to run
vab CI / vab-compiles-v-examples (push) Waiting to run
vab CI / v-compiles-os-android (push) Waiting to run
json decoder benchmark CI / json-encode-benchmark (push) Has been cancelled
decoder2: add support for decoding utf-16 surrogates, produced by some JSON encoder implementations (Python, Java, C#) (#25193)
2025-08-30 13:02:39 +03:00
..
decoder2 decoder2: add support for decoding utf-16 surrogates, produced by some JSON encoder implementations (Python, Java, C#) (#25193) 2025-08-30 13:02:39 +03:00
decoder_tests x.json2: pre-arranging the replacement of the decoder, to match https://github.com/enghitalo/v/tree/decoder2_to_json2 (#22729) 2024-11-02 10:57:18 +02:00
strict docs: put some more dots in the doc comments of public APIs, found by find_doc_comments_with_no_dots.v 2025-07-02 16:12:03 +03:00
tests decoder2: improve checker with better EOF detection (#25075) 2025-08-09 16:38:58 +03:00
count.v all: fix typos (#21089) 2024-03-25 12:18:27 +02:00
count_test.v fmt: remove the prefixed module name of const names, that are in the same module (related #22183) (#22185) 2024-09-10 11:25:56 +03:00
decoder.v checker: fix sumtype variant option type mismatch (#23659) 2025-02-07 16:48:22 +02:00
decoder_deprecated.v json2.decoder2: prepare decoder in json2 to be replaced by json2.decode2 (#23078) 2024-12-06 19:47:17 +02:00
encoder.v docs: put some more dots in the doc comments of public APIs, found by find_doc_comments_with_no_dots.v 2025-07-02 16:12:03 +03:00
json2.v docs: put some more dots in the doc comments of public APIs, found by find_doc_comments_with_no_dots.v 2025-07-02 16:12:03 +03:00
README.md json2.decoder2: prepare decoder in json2 to be replaced by json2.decode2 (#23078) 2024-12-06 19:47:17 +02:00
scanner.v docs: put some more dots in the doc comments of public APIs, found by find_doc_comments_with_no_dots.v 2025-07-02 16:12:03 +03:00
scanner_test.v x.json2: fix "\\" scanner bug, disallow (ch < 0x20) unescaped control characters (#23954) 2025-03-16 13:38:40 +02:00
types.v decoder2: improve checker with better EOF detection (#25075) 2025-08-09 16:38:58 +03:00

The name json2 was chosen to avoid any unwanted potential conflicts with the existing codegen tailored for the main json module which is powered by CJSON.

x.json2 is an experimental JSON parser written from scratch on V.

Usage

encode[T]

import x.json2
import time

struct Person {
mut:
	name     string
	age      ?int = 20
	birthday time.Time
	deathday ?time.Time
}

fn main() {
	mut person := Person{
		name:     'Bob'
		birthday: time.now()
	}
	person_json := json2.encode[Person](person)
	// person_json == {"name": "Bob", "age": 20, "birthday": "2022-03-11T13:54:25.000Z"}
}

decode[T]

import x.json2
import time

struct Person {
mut:
	name     string
	age      ?int = 20
	birthday time.Time
	deathday ?time.Time
}

fn main() {
	resp := '{"name": "Bob", "age": 20, "birthday": "${time.now()}"}'
	person := json2.decode[Person](resp)!
	/*
	struct Person {
      mut:
          name "Bob"
          age  20
          birthday "2022-03-11 13:54:25"
      }
	*/
}

decode[T] is smart and can auto-convert the types of struct fields - this means examples below will have the same result

json2.decode[Person]('{"name": "Bob", "age": 20, "birthday": "2022-03-11T13:54:25.000Z"}')!
json2.decode[Person]('{"name": "Bob", "age": 20, "birthday": "2022-03-11 13:54:25.000"}')!
json2.decode[Person]('{"name": "Bob", "age": "20", "birthday": 1647006865}')!
json2.decode[Person]('{"name": "Bob", "age": "20", "birthday": "1647006865"}}')!

raw decode

import x.json2
import net.http

fn main() {
	resp := http.get('https://reqres.in/api/products/1')!

	// This returns an Any type
	raw_product := json2.decode[json2.Any](resp.body)!
}

Casting Any type / Navigating

import x.json2
import net.http

fn main() {
	resp := http.get('https://reqres.in/api/products/1')!

	raw_product := json2.decode[json2.Any](resp.body)!

	product := raw_product.as_map()
	data := product['data'] as map[string]json2.Any

	id := data['id'].int() // 1
	name := data['name'].str() // cerulean
	year := data['year'].int() // 2000
}

Constructing an Any type

import x.json2

fn main() {
	mut me := map[string]json2.Any{}
	me['name'] = 'Bob'
	me['age'] = 18

	mut arr := []json2.Any{}
	arr << 'rock'
	arr << 'papers'
	arr << json2.null
	arr << 12

	me['interests'] = arr

	mut pets := map[string]json2.Any{}
	pets['Sam'] = 'Maltese Shitzu'
	me['pets'] = pets

	// Stringify to JSON
	println(me.str())
	//{
	//   "name":"Bob",
	//   "age":18,
	//   "interests":["rock","papers","scissors",null,12],
	//   "pets":{"Sam":"Maltese"}
	//}
}

Null Values

x.json2 has a separate Null type for differentiating an undefined value and a null value. To verify that the field you're accessing is a Null, use [typ] is json2.Null.

fn (mut p Person) from_json(f json2.Any) {
    obj := f.as_map()
    if obj['age'] is json2.Null {
        // use a default value
        p.age = 10
    }
}

Casting a value to an incompatible type

x.json2 provides methods for turning Any types into usable types. The following list shows the possible outputs when casting a value to an incompatible type.

  1. Casting non-array values as array (arr()) will return an array with the value as the content.
  2. Casting non-map values as map (as_map()) will return a map with the value as the content.
  3. Casting non-string values to string (str()) will return the JSON string representation of the value.
  4. Casting non-numeric values to int/float (int()/i64()/f32()/f64()) will return zero.

Encoding using string builder instead of []u8

To be more performant, json2, in PR 20052, decided to use buffers directly instead of Writers. If you want to use Writers you can follow the steps below:

mut sb := strings.new_builder(64)
mut buffer := []u8{}

json2.encode_value(<some value to be encoded here>, mut buffer)!

sb.write(buffer)!

unsafe { buffer.free() }