mirror of
https://github.com/nlohmann/json.git
synced 2026-10-10 16:37:14 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cbfd60e2d | ||
|
|
f8b47ff6f6 | ||
|
|
07ec1a4e31 |
No files matched your search
@@ -149,8 +149,10 @@ make build -C docs/mkdocs # strict build: fails on broken links, anchor
|
|||||||
make check_mermaid -C docs/mkdocs # checks the Mermaid diagrams (requires Node.js)
|
make check_mermaid -C docs/mkdocs # checks the Mermaid diagrams (requires Node.js)
|
||||||
```
|
```
|
||||||
|
|
||||||
A new API page also needs an entry in [`docs/docset/docSet.sql`](https://github.com/nlohmann/json/blob/develop/docs/docset/docSet.sql),
|
The search index of the docset is generated from [`mkdocs.yml`](https://github.com/nlohmann/json/blob/develop/docs/mkdocs/mkdocs.yml)
|
||||||
the search index of the docset; `make build` reports missing entries.
|
and each page's title (H1) and declaration by
|
||||||
|
[`docs/docset/generate_docset.py`](https://github.com/nlohmann/json/blob/develop/docs/docset/generate_docset.py);
|
||||||
|
`make build` reports API pages that cannot be classified.
|
||||||
|
|
||||||
### Amalgamate the source code
|
### Amalgamate the source code
|
||||||
|
|
||||||
|
|||||||
+11
-55
@@ -1,48 +1,24 @@
|
|||||||
SHELL=/usr/bin/env bash
|
SHELL=/usr/bin/env bash
|
||||||
SED ?= $(shell which gsed 2>/dev/null || which sed)
|
PYTHON=../mkdocs/venv/bin/python3
|
||||||
|
|
||||||
MKDOCS_PAGES=$(shell cd ../mkdocs/docs/ && find * -type f -name '*.md' | sort)
|
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: JSON_for_Modern_C++.tgz
|
all: JSON_for_Modern_C++.tgz
|
||||||
|
|
||||||
docSet.dsidx: docSet.sql
|
# generate the search index (the docset target does this itself, this is
|
||||||
# generate index
|
# only handy for inspecting the index)
|
||||||
sqlite3 docSet.dsidx <docSet.sql
|
docSet.dsidx: generate_docset.py
|
||||||
|
$(PYTHON) generate_docset.py index docSet.dsidx
|
||||||
|
|
||||||
JSON_for_Modern_C++.docset: Info.plist docSet.dsidx
|
# build the documentation and turn it into a self-contained docset
|
||||||
rm -fr JSON_for_Modern_C++.docset JSON_for_Modern_C++.tgz
|
.PHONY: JSON_for_Modern_C++.docset
|
||||||
mkdir -p JSON_for_Modern_C++.docset/Contents/Resources/Documents/
|
JSON_for_Modern_C++.docset:
|
||||||
cp icon*.png JSON_for_Modern_C++.docset
|
|
||||||
cp Info.plist JSON_for_Modern_C++.docset/Contents
|
|
||||||
# build and copy documentation
|
|
||||||
$(MAKE) install_venv -C ../mkdocs
|
$(MAKE) install_venv -C ../mkdocs
|
||||||
$(MAKE) build -C ../mkdocs
|
$(MAKE) build -C ../mkdocs
|
||||||
cp -r ../mkdocs/site/* JSON_for_Modern_C++.docset/Contents/Resources/Documents
|
$(PYTHON) generate_docset.py docset ../mkdocs/site .
|
||||||
# patch CSS to hide navigation items
|
|
||||||
echo -e "\n\nheader, footer, nav.md-tabs, nav.md-tabs--active, div.md-sidebar--primary, a.md-content__button { display: none; }" >> "$$(ls JSON_for_Modern_C++.docset/Contents/Resources/Documents/assets/stylesheets/main.*.min.css)"
|
|
||||||
# fix spacing
|
|
||||||
echo -e "\n\ndiv.md-sidebar div.md-sidebar--secondary, div.md-main__inner { top: 0; margin-top: 0 }" >> "$$(ls JSON_for_Modern_C++.docset/Contents/Resources/Documents/assets/stylesheets/main.*.min.css)"
|
|
||||||
# remove "JSON for Modern C++" from page titles (fallback)
|
|
||||||
find JSON_for_Modern_C++.docset/Contents/Resources/Documents -type f -exec $(SED) -i 's| - JSON for Modern C++</title>|</title>|' {} +
|
|
||||||
# replace page titles with name from index, if available
|
|
||||||
for page in $(MKDOCS_PAGES); do \
|
|
||||||
case "$$page" in \
|
|
||||||
*/index.md) path=$${page/\/index.md/} ;; \
|
|
||||||
*) path=$${page/.md/} ;; \
|
|
||||||
esac; \
|
|
||||||
title=$$(sqlite3 docSet.dsidx "SELECT name FROM searchIndex WHERE path='$$path/index.html'" | tr '\n' ',' | $(SED) -e 's/,/, /g' -e 's/, $$/\n/'); \
|
|
||||||
if [ "x$$title" != "x" ]; then \
|
|
||||||
$(SED) -i "s%<title>.*</title>%<title>$$title</title>%" "JSON_for_Modern_C++.docset/Contents/Resources/Documents/$$path/index.html"; \
|
|
||||||
fi \
|
|
||||||
done
|
|
||||||
# clean up
|
|
||||||
rm JSON_for_Modern_C++.docset/Contents/Resources/Documents/sitemap.*
|
|
||||||
# copy index
|
|
||||||
cp docSet.dsidx JSON_for_Modern_C++.docset/Contents/Resources/
|
|
||||||
|
|
||||||
|
.PHONY: JSON_for_Modern_C++.tgz
|
||||||
JSON_for_Modern_C++.tgz: JSON_for_Modern_C++.docset
|
JSON_for_Modern_C++.tgz: JSON_for_Modern_C++.docset
|
||||||
tar --exclude='.DS_Store' -cvzf JSON_for_Modern_C++.tgz JSON_for_Modern_C++.docset
|
$(PYTHON) generate_docset.py tgz .
|
||||||
|
|
||||||
# install docset for Zeal documentation browser (https://zealdocs.org/)
|
# install docset for Zeal documentation browser (https://zealdocs.org/)
|
||||||
.PHONY: install_docset_zeal
|
.PHONY: install_docset_zeal
|
||||||
@@ -52,26 +28,6 @@ install_docset_zeal: JSON_for_Modern_C++.docset
|
|||||||
mkdir -p $$docset_root; \
|
mkdir -p $$docset_root; \
|
||||||
cp -r JSON_for_Modern_C++.docset $$docset_root/
|
cp -r JSON_for_Modern_C++.docset $$docset_root/
|
||||||
|
|
||||||
# both targets below compare the docset search index with the mkdocs page
|
|
||||||
# set. They share the same normalization (docs/foo/index.md and
|
|
||||||
# docs/foo.md both become foo/index.html, the URL mkdocs itself would
|
|
||||||
# give the page; the top-level index.md is excluded, as it is not part
|
|
||||||
# of the hand-curated docSet.sql) and use comm(1) on two sorted lists
|
|
||||||
# instead of running a sqlite3 query, or an O(n*m) nested shell loop,
|
|
||||||
# once per page.
|
|
||||||
DOCSET_INDEX_PATHS=$(shell sqlite3 docSet.dsidx "SELECT DISTINCT path FROM searchIndex" | sort)
|
|
||||||
DOCSET_PAGE_PATHS=$(shell echo '$(MKDOCS_PAGES)' | tr ' ' '\n' | grep -v '^index\.md$$' | $(SED) -E 's@/index\.md$$@/index.html@; s@\.md$$@/index.html@' | sort)
|
|
||||||
|
|
||||||
# list mkdocs pages missing from the docset index
|
|
||||||
.PHONY: list_missing_pages
|
|
||||||
list_missing_pages: docSet.dsidx
|
|
||||||
@comm -23 <(echo '$(DOCSET_PAGE_PATHS)' | tr ' ' '\n') <(echo '$(DOCSET_INDEX_PATHS)' | tr ' ' '\n')
|
|
||||||
|
|
||||||
# list paths in the docset index without a corresponding mkdocs page
|
|
||||||
.PHONY: list_removed_paths
|
|
||||||
list_removed_paths: docSet.dsidx
|
|
||||||
@comm -13 <(echo '$(DOCSET_PAGE_PATHS)' | tr ' ' '\n') <(echo '$(DOCSET_INDEX_PATHS)' | tr ' ' '\n')
|
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -f docSet.dsidx
|
rm -f docSet.dsidx
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ The folder contains the required files to create a [docset](https://kapeli.com/d
|
|||||||
documentation browsers like [Dash](https://kapeli.com/dash), [Velocity](https://velocity.silverlakesoftware.com), or
|
documentation browsers like [Dash](https://kapeli.com/dash), [Velocity](https://velocity.silverlakesoftware.com), or
|
||||||
[Zeal](https://zealdocs.org).
|
[Zeal](https://zealdocs.org).
|
||||||
|
|
||||||
The docset can be created with
|
The docset (pages and search index) is generated by `generate_docset.py` from the mkdocs site and `mkdocs.yml`. It
|
||||||
|
can be created with
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
make JSON_for_Modern_C++.docset
|
make JSON_for_Modern_C++.docset
|
||||||
@@ -12,6 +13,7 @@ make JSON_for_Modern_C++.docset
|
|||||||
|
|
||||||
The generated folder `JSON_for_Modern_C++.docset` can then be opened in the documentation browser. `make all` builds a
|
The generated folder `JSON_for_Modern_C++.docset` can then be opened in the documentation browser. `make all` builds a
|
||||||
`JSON_for_Modern_C++.tgz` archive instead, and `make install_docset_zeal` installs the docset for Zeal directly.
|
`JSON_for_Modern_C++.tgz` archive instead, and `make install_docset_zeal` installs the docset for Zeal directly.
|
||||||
|
`make docSet.dsidx` builds only the search index.
|
||||||
|
|
||||||
A recent version is also part of the [Dash user contributions](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B).
|
A recent version is also part of the [Dash user contributions](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B).
|
||||||
|
|
||||||
|
|||||||
@@ -1,289 +0,0 @@
|
|||||||
DROP TABLE IF EXISTS searchIndex;
|
|
||||||
CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT);
|
|
||||||
CREATE UNIQUE INDEX anchor ON searchIndex (name, type, path);
|
|
||||||
|
|
||||||
-- API
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('adl_serializer', 'Class', 'api/adl_serializer/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('adl_serializer::from_json', 'Function', 'api/adl_serializer/from_json/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('adl_serializer::to_json', 'Function', 'api/adl_serializer/to_json/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype', 'Class', 'api/byte_container_with_subtype/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::byte_container_with_subtype', 'Constructor', 'api/byte_container_with_subtype/byte_container_with_subtype/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::clear_subtype', 'Method', 'api/byte_container_with_subtype/clear_subtype/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::has_subtype', 'Method', 'api/byte_container_with_subtype/has_subtype/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::operator!=', 'Operator', 'api/byte_container_with_subtype/operator_ne/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::operator==', 'Operator', 'api/byte_container_with_subtype/operator_eq/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::set_subtype', 'Method', 'api/byte_container_with_subtype/set_subtype/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::subtype', 'Method', 'api/byte_container_with_subtype/subtype/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json', 'Class', 'api/basic_json/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('format_as', 'Function', 'api/basic_json/format_as/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::accept', 'Function', 'api/basic_json/accept/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array', 'Function', 'api/basic_json/array/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array_t', 'Type', 'api/basic_json/array_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::as_base_class', 'Method', 'api/basic_json/as_base_class/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::at', 'Method', 'api/basic_json/at/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::back', 'Method', 'api/basic_json/back/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::basic_json', 'Constructor', 'api/basic_json/basic_json/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::begin', 'Method', 'api/basic_json/begin/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::binary', 'Function', 'api/basic_json/binary/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::binary_t', 'Type', 'api/basic_json/binary_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::boolean_t', 'Type', 'api/basic_json/boolean_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cbegin', 'Method', 'api/basic_json/cbegin/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cbor_tag_handler_t', 'Enum', 'api/basic_json/cbor_tag_handler_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cend', 'Method', 'api/basic_json/cend/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::clear', 'Method', 'api/basic_json/clear/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::contains', 'Method', 'api/basic_json/contains/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::count', 'Method', 'api/basic_json/count/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::crbegin', 'Method', 'api/basic_json/crbegin/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::crend', 'Method', 'api/basic_json/crend/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::default_object_comparator_t', 'Type', 'api/basic_json/default_object_comparator_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::diff', 'Function', 'api/basic_json/diff/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::dump', 'Method', 'api/basic_json/dump/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::emplace', 'Method', 'api/basic_json/emplace/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::emplace_back', 'Method', 'api/basic_json/emplace_back/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::empty', 'Method', 'api/basic_json/empty/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::end', 'Method', 'api/basic_json/end/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::end_pos', 'Method', 'api/basic_json/end_pos/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::erase', 'Method', 'api/basic_json/erase/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::error_handler_t', 'Enum', 'api/basic_json/error_handler_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::exception', 'Class', 'api/basic_json/exception/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::find', 'Method', 'api/basic_json/find/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::flatten', 'Method', 'api/basic_json/flatten/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bjdata', 'Function', 'api/basic_json/from_bjdata/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bson', 'Function', 'api/basic_json/from_bson/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_cbor', 'Function', 'api/basic_json/from_cbor/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_msgpack', 'Function', 'api/basic_json/from_msgpack/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bon8', 'Function', 'api/basic_json/from_bon8/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_ubjson', 'Function', 'api/basic_json/from_ubjson/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::front', 'Method', 'api/basic_json/front/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get', 'Method', 'api/basic_json/get/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_allocator', 'Function', 'api/basic_json/get_allocator/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_binary', 'Method', 'api/basic_json/get_binary/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_ptr', 'Method', 'api/basic_json/get_ptr/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_ref', 'Method', 'api/basic_json/get_ref/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_to', 'Method', 'api/basic_json/get_to/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::input_format_t', 'Enum', 'api/basic_json/input_format_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::insert', 'Method', 'api/basic_json/insert/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::invalid_iterator', 'Class', 'api/basic_json/invalid_iterator/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_array', 'Method', 'api/basic_json/is_array/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_binary', 'Method', 'api/basic_json/is_binary/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_boolean', 'Method', 'api/basic_json/is_boolean/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_discarded', 'Method', 'api/basic_json/is_discarded/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_null', 'Method', 'api/basic_json/is_null/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number', 'Method', 'api/basic_json/is_number/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number_float', 'Method', 'api/basic_json/is_number_float/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number_integer', 'Method', 'api/basic_json/is_number_integer/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number_unsigned', 'Method', 'api/basic_json/is_number_unsigned/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_object', 'Method', 'api/basic_json/is_object/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_primitive', 'Method', 'api/basic_json/is_primitive/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_string', 'Method', 'api/basic_json/is_string/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_structured', 'Method', 'api/basic_json/is_structured/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::items', 'Method', 'api/basic_json/items/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_base_class_t', 'Type', 'api/basic_json/json_base_class_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_serializer', 'Class', 'api/basic_json/json_serializer/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::max_size', 'Method', 'api/basic_json/max_size/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::merge_patch', 'Method', 'api/basic_json/merge_patch/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::meta', 'Function', 'api/basic_json/meta/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::number_float_t', 'Type', 'api/basic_json/number_float_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::number_integer_t', 'Type', 'api/basic_json/number_integer_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::number_unsigned_t', 'Type', 'api/basic_json/number_unsigned_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::object', 'Function', 'api/basic_json/object/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::object_comparator_t', 'Type', 'api/basic_json/object_comparator_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::object_t', 'Type', 'api/basic_json/object_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator ValueType', 'Operator', 'api/basic_json/operator_ValueType/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator value_t', 'Operator', 'api/basic_json/operator_value_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator[]', 'Operator', 'api/basic_json/operator[]/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator=', 'Operator', 'api/basic_json/operator=/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator+=', 'Operator', 'api/basic_json/operator+=/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator==', 'Operator', 'api/basic_json/operator_eq/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator!=', 'Operator', 'api/basic_json/operator_ne/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator<', 'Operator', 'api/basic_json/operator_lt/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator<=', 'Operator', 'api/basic_json/operator_le/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator>', 'Operator', 'api/basic_json/operator_gt/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator>=', 'Operator', 'api/basic_json/operator_ge/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator<=>', 'Operator', 'api/basic_json/operator_spaceship/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::out_of_range', 'Class', 'api/basic_json/out_of_range/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::other_error', 'Class', 'api/basic_json/other_error/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parse', 'Function', 'api/basic_json/parse/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parse_error', 'Class', 'api/basic_json/parse_error/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parse_event_t', 'Enum', 'api/basic_json/parse_event_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parser_callback_t', 'Type', 'api/basic_json/parser_callback_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::patch', 'Method', 'api/basic_json/patch/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::patch_inplace', 'Method', 'api/basic_json/patch_inplace/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::push_back', 'Method', 'api/basic_json/push_back/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::rbegin', 'Method', 'api/basic_json/rbegin/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::rend', 'Method', 'api/basic_json/rend/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::sax_parse', 'Function', 'api/basic_json/sax_parse/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::size', 'Method', 'api/basic_json/size/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::start_pos', 'Method', 'api/basic_json/start_pos/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::string_t', 'Type', 'api/basic_json/string_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::swap', 'Method', 'api/basic_json/swap/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::type', 'Method', 'api/basic_json/type/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::type_error', 'Class', 'api/basic_json/type_error/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::type_name', 'Method', 'api/basic_json/type_name/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::unflatten', 'Method', 'api/basic_json/unflatten/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::update', 'Method', 'api/basic_json/update/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bjdata', 'Function', 'api/basic_json/to_bjdata/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bson', 'Function', 'api/basic_json/to_bson/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_cbor', 'Function', 'api/basic_json/to_cbor/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_msgpack', 'Function', 'api/basic_json/to_msgpack/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bon8', 'Function', 'api/basic_json/to_bon8/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_string', 'Method', 'api/basic_json/to_string/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_ubjson', 'Function', 'api/basic_json/to_ubjson/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value', 'Method', 'api/basic_json/value/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value_t', 'Enum', 'api/basic_json/value_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::with_t', 'Type', 'api/basic_json/with_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::~basic_json', 'Method', 'api/basic_json/~basic_json/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json', 'Class', 'api/json/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer', 'Class', 'api/json_pointer/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::back', 'Method', 'api/json_pointer/back/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::empty', 'Method', 'api/json_pointer/empty/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::front', 'Method', 'api/json_pointer/front/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::json_pointer', 'Constructor', 'api/json_pointer/json_pointer/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator==', 'Operator', 'api/json_pointer/operator_eq/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator!=', 'Operator', 'api/json_pointer/operator_ne/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator/', 'Operator', 'api/json_pointer/operator_slash/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator/=', 'Operator', 'api/json_pointer/operator_slasheq/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator string_t', 'Operator', 'api/json_pointer/operator_string_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator<=>', 'Operator', 'api/json_pointer/operator_spaceship/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::parent_pointer', 'Method', 'api/json_pointer/parent_pointer/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::pop_back', 'Method', 'api/json_pointer/pop_back/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::pop_front', 'Method', 'api/json_pointer/pop_front/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::push_back', 'Method', 'api/json_pointer/push_back/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::push_front', 'Method', 'api/json_pointer/push_front/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::string_t', 'Type', 'api/json_pointer/string_t/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::to_string', 'Method', 'api/json_pointer/to_string/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax', 'Class', 'api/json_sax/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::binary', 'Method', 'api/json_sax/binary/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::boolean', 'Method', 'api/json_sax/boolean/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::end_array', 'Method', 'api/json_sax/end_array/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::end_object', 'Method', 'api/json_sax/end_object/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::key', 'Method', 'api/json_sax/key/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::null', 'Method', 'api/json_sax/null/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_float', 'Method', 'api/json_sax/number_float/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_integer', 'Method', 'api/json_sax/number_integer/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_unsigned', 'Method', 'api/json_sax/number_unsigned/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::parse_error', 'Method', 'api/json_sax/parse_error/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::start_array', 'Method', 'api/json_sax/start_array/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::start_object', 'Method', 'api/json_sax/start_object/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::string', 'Method', 'api/json_sax/string/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('operator""_json', 'Literal', 'api/operator_literal_json/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('operator""_json_pointer', 'Literal', 'api/operator_literal_json_pointer/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('operator<<', 'Operator', 'api/operator_ltlt/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('operator>>', 'Operator', 'api/operator_gtgt/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json', 'Class', 'api/ordered_json/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map', 'Class', 'api/ordered_map/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('std::formatter<basic_json>', 'Class', 'api/basic_json/std_formatter/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('std::hash<basic_json>', 'Class', 'api/basic_json/std_hash/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('std::swap<basic_json>', 'Function', 'api/basic_json/std_swap/index.html');
|
|
||||||
|
|
||||||
-- Features
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Arbitrary Type Conversions', 'Guide', 'features/arbitrary_types/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats', 'Guide', 'features/binary_formats/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BJData', 'Guide', 'features/binary_formats/bjdata/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BSON', 'Guide', 'features/binary_formats/bson/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: CBOR', 'Guide', 'features/binary_formats/cbor/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: MessagePack', 'Guide', 'features/binary_formats/messagepack/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BON8', 'Guide', 'features/binary_formats/bon8/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: UBJSON', 'Guide', 'features/binary_formats/ubjson/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Values', 'Guide', 'features/binary_values/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Comments', 'Guide', 'features/comments/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access', 'Guide', 'features/element_access/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Access with default value: value', 'Guide', 'features/element_access/default_value/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Checked access: at', 'Guide', 'features/element_access/checked_access/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Unchecked access: operator[]', 'Guide', 'features/element_access/unchecked_access/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Exceptions', 'Guide', 'home/exceptions/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Migration Guide', 'Guide', 'integration/migration_guide/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: CMake', 'Guide', 'integration/cmake/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Header only', 'Guide', 'integration/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Package Managers', 'Guide', 'integration/package_managers/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Pkg-config', 'Guide', 'integration/pkg-config/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Iterators', 'Guide', 'features/iterators/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON Merge Patch', 'Guide', 'features/merge_patch/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON Patch and Diff', 'Guide', 'features/json_patch/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON Pointer', 'Guide', 'features/json_pointer/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('nlohmann Namespace', 'Guide', 'features/namespace/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Types', 'Guide', 'features/types/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Types: Number Handling', 'Guide', 'features/types/number_handling/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Object Order', 'Guide', 'features/object_order/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Performance', 'Guide', 'features/performance/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing', 'Guide', 'features/parsing/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: JSON Lines', 'Guide', 'features/parsing/json_lines/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: Parser Callbacks', 'Guide', 'features/parsing/parser_callbacks/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: Parsing and Exceptions', 'Guide', 'features/parsing/parse_exceptions/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: SAX Interface', 'Guide', 'features/parsing/sax_interface/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: Untrusted Input', 'Guide', 'features/parsing/untrusted_input/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Runtime Assertions', 'Guide', 'features/assertions/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Specializing enum conversion', 'Guide', 'features/enum_conversion/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Supported Macros', 'Guide', 'features/macros/index.html');
|
|
||||||
|
|
||||||
-- Macros
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_ASSERT', 'Macro', 'api/macros/json_assert/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_BRACE_INIT_COPY_SEMANTICS', 'Macro', 'api/macros/json_brace_init_copy_semantics/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_CATCH_USER', 'Macro', 'api/macros/json_throw_user/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DELETE_DEPRECATED_FUNCTIONS', 'Macro', 'api/macros/json_delete_deprecated_functions/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTICS', 'Macro', 'api/macros/json_diagnostics/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTIC_POSITIONS', 'Macro', 'api/macros/json_diagnostic_positions/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_ENUM_SERIALIZATION', 'Macro', 'api/macros/json_disable_enum_serialization/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_TUPLE_REFERENCE_CONVERSION', 'Macro', 'api/macros/json_disable_tuple_reference_conversion/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_11', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_14', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_17', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_20', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_23', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_26', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_EXPERIMENTAL_FILESYSTEM', 'Macro', 'api/macros/json_has_filesystem/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_FILESYSTEM', 'Macro', 'api/macros/json_has_filesystem/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_RANGES', 'Macro', 'api/macros/json_has_ranges/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_STATIC_RTTI', 'Macro', 'api/macros/json_has_static_rtti/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_STD_FORMAT', 'Macro', 'api/macros/json_has_std_format/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_THREE_WAY_COMPARISON', 'Macro', 'api/macros/json_has_three_way_comparison/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NOEXCEPTION', 'Macro', 'api/macros/json_noexception/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_AUTOMATIC_UDLS', 'Macro', 'api/macros/json_no_automatic_udls/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_IO', 'Macro', 'api/macros/json_no_io/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_THREAD_LOCAL', 'Macro', 'api/macros/json_no_thread_local/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_PRECISE_STREAM_POSITION', 'Macro', 'api/macros/json_precise_stream_position/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_LIBRARY_VERSION_CHECK', 'Macro', 'api/macros/json_skip_library_version_check/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_UNSUPPORTED_COMPILER_CHECK', 'Macro', 'api/macros/json_skip_unsupported_compiler_check/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_STRICT_BINARY_UTF8', 'Macro', 'api/macros/json_strict_binary_utf8/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_STRICT_NUL_HANDLING', 'Macro', 'api/macros/json_strict_nul_handling/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_THROW_USER', 'Macro', 'api/macros/json_throw_user/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_TRY_USER', 'Macro', 'api/macros/json_throw_user/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_GLOBAL_UDLS', 'Macro', 'api/macros/json_use_global_udls/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_IMPLICIT_CONVERSIONS', 'Macro', 'api/macros/json_use_implicit_conversions/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON', 'Macro', 'api/macros/json_use_legacy_discarded_value_comparison/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS', 'Macro', 'api/macros/json_use_objects_for_enum_keyed_maps/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_SIMDUTF', 'Macro', 'api/macros/json_use_simdutf/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Macros', 'Macro', 'api/macros/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_type_non_intrusive/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_type_non_intrusive/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_type_non_intrusive/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE', 'Macro', 'api/macros/nlohmann_json_namespace/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE_BEGIN', 'Macro', 'api/macros/nlohmann_json_namespace_begin/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE_END', 'Macro', 'api/macros/nlohmann_json_namespace_begin/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE_NO_VERSION', 'Macro', 'api/macros/nlohmann_json_namespace_no_version/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_SERIALIZE_ENUM', 'Macro', 'api/macros/nlohmann_json_serialize_enum/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_SERIALIZE_ENUM_STRICT', 'Macro', 'api/macros/nlohmann_json_serialize_enum_strict/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MAJOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MINOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_PATCH', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
|
||||||
Executable
+532
@@ -0,0 +1,532 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""Generate the Dash docset search index from the mkdocs sources."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import glob
|
||||||
|
import hashlib
|
||||||
|
import html
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import sqlite3
|
||||||
|
import sys
|
||||||
|
import tarfile
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
MKDOCS_YML = os.path.join(HERE, '..', 'mkdocs', 'mkdocs.yml')
|
||||||
|
PAGES = os.path.join(HERE, '..', 'mkdocs', 'docs')
|
||||||
|
|
||||||
|
# api pages whose (name, type) cannot be derived by the heuristics
|
||||||
|
OVERRIDES = {
|
||||||
|
}
|
||||||
|
|
||||||
|
DOCSET = 'JSON_for_Modern_C++.docset'
|
||||||
|
TITLE_SUFFIX = ' - JSON for Modern C++</title>'
|
||||||
|
|
||||||
|
# CSS rules appended to the stylesheet: hide navigation items and fix spacing
|
||||||
|
# hide the navigation (the documentation browser has its own); Material's class selectors would win over element
|
||||||
|
# selectors, hence the classes and !important
|
||||||
|
CSS_PATCH = (
|
||||||
|
'\n\n.md-header, .md-footer, .md-tabs, .md-sidebar--primary, .md-content__button { display: none !important; }'
|
||||||
|
'\n\n.md-sidebar--secondary, .md-main__inner { top: 0 !important; margin-top: 0 !important; }'
|
||||||
|
)
|
||||||
|
|
||||||
|
USER_AGENT = ('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 '
|
||||||
|
'(KHTML, like Gecko) Chrome/124.0 Safari/537.36')
|
||||||
|
CONTENT_TYPE_EXT = {'image/svg+xml': '.svg', 'image/png': '.png', 'image/jpeg': '.jpg',
|
||||||
|
'image/gif': '.gif', 'image/webp': '.webp'}
|
||||||
|
|
||||||
|
# remote loads that are allowed to remain (URL -> reason)
|
||||||
|
ALLOWED_REMOTE = {
|
||||||
|
# Material only loads this polyfill if the browser has no ResizeObserver
|
||||||
|
'https://unpkg.com/resize-observer-polyfill': 'fallback for browsers without ResizeObserver',
|
||||||
|
}
|
||||||
|
|
||||||
|
problems = []
|
||||||
|
|
||||||
|
|
||||||
|
def problem(page, reason) -> None:
|
||||||
|
"""Record a problem; all problems are reported at the end."""
|
||||||
|
problems.append(f'generate_docset.py: {page}: {reason}')
|
||||||
|
|
||||||
|
|
||||||
|
class Loader(yaml.SafeLoader):
|
||||||
|
"""YAML loader that tolerates the custom tags used in mkdocs.yml."""
|
||||||
|
|
||||||
|
|
||||||
|
Loader.add_multi_constructor('', lambda loader, suffix, node: None)
|
||||||
|
|
||||||
|
|
||||||
|
def walk_nav(items, groups=()):
|
||||||
|
"""Yield (group titles, nav title or None, md path) for all nav leaves."""
|
||||||
|
for item in items:
|
||||||
|
if isinstance(item, str):
|
||||||
|
yield list(groups), None, item
|
||||||
|
elif isinstance(item, dict):
|
||||||
|
for title, value in item.items():
|
||||||
|
if isinstance(value, list):
|
||||||
|
yield from walk_nav(value, groups + (str(title),))
|
||||||
|
else:
|
||||||
|
yield list(groups), str(title), value
|
||||||
|
|
||||||
|
|
||||||
|
def page_path(md_path) -> str:
|
||||||
|
"""Map a markdown path to the HTML path of the rendered page."""
|
||||||
|
if md_path.endswith('/index.md'):
|
||||||
|
return md_path[:-len('index.md')] + 'index.html'
|
||||||
|
return md_path[:-len('.md')] + '/index.html'
|
||||||
|
|
||||||
|
|
||||||
|
def read_page(md_path) -> str:
|
||||||
|
"""Read a page (resolving a snippet include), return '' if it does not exist."""
|
||||||
|
try:
|
||||||
|
with open(os.path.join(PAGES, md_path), encoding='utf-8') as f:
|
||||||
|
text = f.read()
|
||||||
|
m = re.match(r'--8<-- "(.+)"\s*$', text)
|
||||||
|
if m:
|
||||||
|
with open(os.path.join(PAGES, m.group(1)), encoding='utf-8') as f:
|
||||||
|
text = f.read()
|
||||||
|
return text
|
||||||
|
except OSError:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
def clean(text) -> str:
|
||||||
|
"""Strip tags and entities from a heading and normalize whitespace."""
|
||||||
|
text = text.replace('\\>', '\x00') # escaped '>' is not the end of a tag
|
||||||
|
text = html.unescape(re.sub(r'</?[a-zA-Z][^>]*>', '', text)).replace('\x00', '>')
|
||||||
|
return re.sub(r'\s+', ' ', text).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def strip_fences(text) -> str:
|
||||||
|
"""Remove fenced code blocks (their lines may start with '# ')."""
|
||||||
|
return re.sub(r'^(```|~~~).*?^\1[^\n]*$', '', text, flags=re.M | re.S)
|
||||||
|
|
||||||
|
|
||||||
|
def get_h1(text):
|
||||||
|
"""Return the cleaned first H1 of a page or None."""
|
||||||
|
body = strip_fences(text)
|
||||||
|
m = re.search(r'^# (.+)$', body, flags=re.M)
|
||||||
|
if m:
|
||||||
|
return clean(m.group(1))
|
||||||
|
m = re.search(r'<h1>(.*?)</h1>', body, flags=re.S)
|
||||||
|
return clean(m.group(1)) if m else None
|
||||||
|
|
||||||
|
|
||||||
|
def split_top_level(text, seps=','):
|
||||||
|
"""Split at separators that are not nested in <>, () or []."""
|
||||||
|
parts, depth, current = [], 0, ''
|
||||||
|
for c in text:
|
||||||
|
if c in '<([':
|
||||||
|
depth += 1
|
||||||
|
elif c in '>)]':
|
||||||
|
depth -= 1
|
||||||
|
if c in seps and depth <= 0:
|
||||||
|
parts.append(current)
|
||||||
|
current = ''
|
||||||
|
else:
|
||||||
|
current += c
|
||||||
|
parts.append(current)
|
||||||
|
return [p.strip() for p in parts if p.strip()]
|
||||||
|
|
||||||
|
|
||||||
|
OPERATOR_SYMBOLS = ('<=>', '<<', '>>', '<=', '>=', '<', '>')
|
||||||
|
|
||||||
|
|
||||||
|
def api_names(h1) -> list:
|
||||||
|
"""Derive the entry names from the H1 of an api page."""
|
||||||
|
# hide the angle brackets of operators from the nesting detection
|
||||||
|
for i, sym in enumerate(OPERATOR_SYMBOLS):
|
||||||
|
h1 = h1.replace('operator' + sym, f'operator\x01{i}\x01')
|
||||||
|
names = []
|
||||||
|
for part in split_top_level(h1):
|
||||||
|
name = part.replace('\\', '').replace('nlohmann::', '')
|
||||||
|
name = re.sub(r'\x01(\d)\x01', lambda m: OPERATOR_SYMBOLS[int(m.group(1))], name)
|
||||||
|
# drop qualifiers like "operator<<(basic_json)", but keep "operator()"
|
||||||
|
if not name.endswith('operator()'):
|
||||||
|
name = re.sub(r'(?<=\w|[<>=!+\-*/\[\]])\([^()]*\)$', '', name)
|
||||||
|
if name not in names:
|
||||||
|
names.append(name)
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
def first_cpp_block(text):
|
||||||
|
"""Return the first ```cpp block following the H1."""
|
||||||
|
m = re.search(r'^# .*?^```cpp\n(.*?)^```', text, flags=re.M | re.S)
|
||||||
|
return m.group(1) if m else None
|
||||||
|
|
||||||
|
|
||||||
|
def api_type(name, decl):
|
||||||
|
"""Determine the Dash entry type from name and declaration."""
|
||||||
|
parts = name.split('::')
|
||||||
|
last = parts[-1]
|
||||||
|
if name.startswith('operator""'):
|
||||||
|
return 'Literal'
|
||||||
|
if last.startswith('operator'):
|
||||||
|
return 'Operator'
|
||||||
|
if decl is None:
|
||||||
|
return None
|
||||||
|
if re.search(r'\benum\b', decl):
|
||||||
|
return 'Enum'
|
||||||
|
if re.search(r'^\s*(template\s*<.*>\s*)?(class|struct)\s+\w+\s*(final\b|[:{;<]|$)', decl, flags=re.M):
|
||||||
|
return 'Class'
|
||||||
|
if re.search(r'\busing\s+\w+\s*=', decl) or 'typedef' in decl:
|
||||||
|
return 'Type'
|
||||||
|
if len(parts) > 1 and last == parts[-2]:
|
||||||
|
return 'Constructor'
|
||||||
|
if last.startswith('~'):
|
||||||
|
return 'Method'
|
||||||
|
if re.search(r'\bstatic\b', decl) or len(parts) == 1 or parts[0] == 'std':
|
||||||
|
return 'Function'
|
||||||
|
return 'Method'
|
||||||
|
|
||||||
|
|
||||||
|
def macro_names(h1) -> list:
|
||||||
|
"""Split the H1 of a macro page into macro names."""
|
||||||
|
return [n.strip() for n in re.split(r'[,/]', h1) if n.strip()]
|
||||||
|
|
||||||
|
|
||||||
|
def api_entries(md_path):
|
||||||
|
"""Return the (name, type) pairs of an api page."""
|
||||||
|
if md_path in OVERRIDES:
|
||||||
|
return OVERRIDES[md_path]
|
||||||
|
if md_path == 'api/macros/index.md':
|
||||||
|
return [('Macros', 'Macro')]
|
||||||
|
text = read_page(md_path)
|
||||||
|
h1 = get_h1(text)
|
||||||
|
if not h1:
|
||||||
|
problem(md_path, 'no H1 found')
|
||||||
|
return []
|
||||||
|
if md_path.startswith('api/macros/'):
|
||||||
|
return [(n, 'Macro') for n in macro_names(h1)]
|
||||||
|
names = api_names(h1)
|
||||||
|
if not names:
|
||||||
|
problem(md_path, 'no names found')
|
||||||
|
return []
|
||||||
|
decl = first_cpp_block(text)
|
||||||
|
result = []
|
||||||
|
for name in names:
|
||||||
|
kind = api_type(name, decl)
|
||||||
|
if kind is None:
|
||||||
|
problem(md_path, f'no type determinable for {name}')
|
||||||
|
else:
|
||||||
|
result.append((name, kind))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def guide_entries(nav):
|
||||||
|
"""Yield (name, type, md path) for all non-api nav pages."""
|
||||||
|
for groups, title, md_path in walk_nav(nav):
|
||||||
|
if md_path.startswith('api/') or md_path == 'index.md':
|
||||||
|
continue
|
||||||
|
if not md_path.endswith('.md'):
|
||||||
|
continue
|
||||||
|
groups = groups[1:] # drop the top-level tab
|
||||||
|
if title is None:
|
||||||
|
title = get_h1(read_page(md_path))
|
||||||
|
if not title:
|
||||||
|
problem(md_path, 'no title found')
|
||||||
|
continue
|
||||||
|
# "Parsing: Parsing Untrusted Input" -> "Parsing: Untrusted Input"
|
||||||
|
if groups and title.startswith(groups[-1] + ' ') and title[len(groups[-1]) + 1:][:1].isupper():
|
||||||
|
title = title[len(groups[-1]) + 1:]
|
||||||
|
if md_path.endswith('/index.md') and groups and title == groups[-1]:
|
||||||
|
name = ': '.join(groups)
|
||||||
|
else:
|
||||||
|
name = ': '.join(groups + [title])
|
||||||
|
yield name, 'Guide', md_path
|
||||||
|
|
||||||
|
|
||||||
|
def build_entries() -> list:
|
||||||
|
"""Return the sorted list of (name, type, path) index entries."""
|
||||||
|
nav = load_mkdocs_yml()['nav']
|
||||||
|
|
||||||
|
entries = set()
|
||||||
|
for name, kind, md_path in guide_entries(nav):
|
||||||
|
entries.add((name, kind, page_path(md_path)))
|
||||||
|
|
||||||
|
api_root = os.path.join(PAGES, 'api')
|
||||||
|
on_disk = set()
|
||||||
|
for root, _, files in os.walk(api_root):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith('.md'):
|
||||||
|
rel = os.path.relpath(os.path.join(root, file), PAGES)
|
||||||
|
on_disk.add(rel.replace(os.sep, '/'))
|
||||||
|
for _, _, md_path in walk_nav(nav):
|
||||||
|
if md_path.startswith('api/') and md_path not in on_disk:
|
||||||
|
problem(md_path, 'listed in nav but missing on disk')
|
||||||
|
for md_path in sorted(on_disk):
|
||||||
|
for name, kind in api_entries(md_path):
|
||||||
|
entries.add((name, kind, page_path(md_path)))
|
||||||
|
return sorted(entries)
|
||||||
|
|
||||||
|
|
||||||
|
def write_index(entries, out) -> None:
|
||||||
|
"""Write the entries into a SQLite search index."""
|
||||||
|
if os.path.exists(out):
|
||||||
|
os.remove(out)
|
||||||
|
con = sqlite3.connect(out)
|
||||||
|
con.execute('CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT)')
|
||||||
|
con.execute('CREATE UNIQUE INDEX anchor ON searchIndex (name, type, path)')
|
||||||
|
con.executemany('INSERT INTO searchIndex(name, type, path) VALUES (?, ?, ?)', entries)
|
||||||
|
con.commit()
|
||||||
|
con.close()
|
||||||
|
|
||||||
|
|
||||||
|
def html_files(root):
|
||||||
|
"""Yield all HTML files below root."""
|
||||||
|
for base, _, files in os.walk(root):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith('.html'):
|
||||||
|
yield os.path.join(base, file)
|
||||||
|
|
||||||
|
|
||||||
|
def read(path) -> str:
|
||||||
|
with open(path, encoding='utf-8') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def write(path, text) -> None:
|
||||||
|
with open(path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(text)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_source_widget(docs) -> None:
|
||||||
|
"""Drop data-md-component=source so Material does not query api.github.com for the stars and version."""
|
||||||
|
pattern = re.compile(r'\sdata-md-component=(?:"source"|source\b)')
|
||||||
|
for path in html_files(docs):
|
||||||
|
text = read(path)
|
||||||
|
new = pattern.sub('', text)
|
||||||
|
if new != text:
|
||||||
|
write(path, new)
|
||||||
|
|
||||||
|
|
||||||
|
def patch_titles(docs, entries) -> None:
|
||||||
|
"""Strip the site name from all titles; use the index names where available."""
|
||||||
|
names = {}
|
||||||
|
for name, _, path in entries:
|
||||||
|
names.setdefault(path, []).append(name)
|
||||||
|
for file in html_files(docs):
|
||||||
|
rel = os.path.relpath(file, docs).replace(os.sep, '/')
|
||||||
|
text = read(file).replace(TITLE_SUFFIX, '</title>')
|
||||||
|
if rel in names:
|
||||||
|
title = html.escape(', '.join(names[rel]), quote=False)
|
||||||
|
text = re.sub(r'<title>.*?</title>', lambda _: f'<title>{title}</title>', text, count=1, flags=re.S)
|
||||||
|
write(file, text)
|
||||||
|
|
||||||
|
|
||||||
|
IMG_RE = re.compile(r'<img\b[^>]*>', re.I)
|
||||||
|
ATTR_RE = r'''(?:{0})\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+))'''
|
||||||
|
|
||||||
|
|
||||||
|
def attr(tag, name):
|
||||||
|
"""Return the value of an attribute in a tag or None."""
|
||||||
|
m = re.search(r'(?<![\w-])' + ATTR_RE.format(name), tag, flags=re.I)
|
||||||
|
return next(g for g in m.groups() if g is not None) if m else None
|
||||||
|
|
||||||
|
|
||||||
|
def is_remote(url) -> bool:
|
||||||
|
return re.match(r'(https?:)?//', url.strip(), flags=re.I) is not None
|
||||||
|
|
||||||
|
|
||||||
|
def download(url, docs) -> str:
|
||||||
|
"""Download url into assets/external and return the path relative to docs."""
|
||||||
|
u = urllib.parse.urlparse(url if not url.startswith('//') else 'https:' + url)
|
||||||
|
if u.scheme.lower() not in ('http', 'https'):
|
||||||
|
raise ValueError(f'not an http(s) URL: {url}')
|
||||||
|
req = urllib.request.Request(u.geturl(), headers={'User-Agent': USER_AGENT})
|
||||||
|
# (the scheme is checked above)
|
||||||
|
with urllib.request.urlopen(req, timeout=20) as r: # nosec B310
|
||||||
|
data = r.read()
|
||||||
|
ctype = r.headers.get_content_type()
|
||||||
|
path = urllib.parse.unquote(u.path).lstrip('/')
|
||||||
|
ext = os.path.splitext(path)[1]
|
||||||
|
if u.query or not ext or path.endswith('/'):
|
||||||
|
digest = hashlib.sha1(url.encode(), usedforsecurity=False).hexdigest()[:12]
|
||||||
|
path = os.path.join(os.path.dirname(path), digest + CONTENT_TYPE_EXT.get(ctype, ext or '.bin'))
|
||||||
|
rel = os.path.normpath(os.path.join('assets', 'external', u.hostname, path))
|
||||||
|
out = os.path.join(docs, rel)
|
||||||
|
os.makedirs(os.path.dirname(out), exist_ok=True)
|
||||||
|
with open(out, 'wb') as f:
|
||||||
|
f.write(data)
|
||||||
|
return rel.replace(os.sep, '/')
|
||||||
|
|
||||||
|
|
||||||
|
def localize_images(docs) -> None:
|
||||||
|
"""Download remote images and rewrite their src; drop them on failure."""
|
||||||
|
cache = {}
|
||||||
|
for file in html_files(docs):
|
||||||
|
text = read(file)
|
||||||
|
|
||||||
|
def repl(m):
|
||||||
|
tag = m.group(0)
|
||||||
|
src = attr(tag, 'src')
|
||||||
|
if src is None or not is_remote(src):
|
||||||
|
return tag
|
||||||
|
if src not in cache:
|
||||||
|
try:
|
||||||
|
cache[src] = download(src, docs)
|
||||||
|
except Exception as e: # noqa: BLE001
|
||||||
|
print(f'generate_docset.py: warning: cannot download {src}: {e}', file=sys.stderr)
|
||||||
|
cache[src] = None
|
||||||
|
if cache[src] is None:
|
||||||
|
return attr(tag, 'alt') or ''
|
||||||
|
local = os.path.relpath(os.path.join(docs, cache[src]), os.path.dirname(file))
|
||||||
|
local = local.replace(os.sep, '/')
|
||||||
|
return re.sub(ATTR_RE.format('src'), lambda _: f'src="{local}"', tag, count=1, flags=re.I)
|
||||||
|
|
||||||
|
new = IMG_RE.sub(repl, text)
|
||||||
|
if new != text:
|
||||||
|
write(file, new)
|
||||||
|
|
||||||
|
|
||||||
|
def load_mkdocs_yml() -> dict:
|
||||||
|
"""Load mkdocs.yml, ignoring tags like !ENV and !!python/name."""
|
||||||
|
with open(MKDOCS_YML, encoding='utf-8') as f:
|
||||||
|
# (Loader is a yaml.SafeLoader)
|
||||||
|
return yaml.load(f, Loader=Loader) # nosec B506
|
||||||
|
|
||||||
|
|
||||||
|
def localize_site_urls(docs, site_url) -> None:
|
||||||
|
"""Load assets that the theme's JavaScript references by absolute site URL from the docset.
|
||||||
|
|
||||||
|
The privacy plugin rewrites the mermaid loader to "<site_url>assets/external/unpkg.com/mermaid@11/...", so the
|
||||||
|
docset would fetch mermaid from the live site. __md_scope is the site root that Material defines in every page.
|
||||||
|
"""
|
||||||
|
pattern = re.compile(r'"' + re.escape(site_url) + r'(assets/[^"]*)"')
|
||||||
|
for base, _, files in os.walk(docs):
|
||||||
|
for file in (f for f in files if f.endswith('.js')):
|
||||||
|
path = os.path.join(base, file)
|
||||||
|
text = read(path)
|
||||||
|
new = pattern.sub(r'new URL("\1",__md_scope).href', text)
|
||||||
|
if new != text:
|
||||||
|
write(path, new)
|
||||||
|
|
||||||
|
|
||||||
|
def remote_loads(docs, site_url) -> list:
|
||||||
|
"""Return 'file: url' strings for resources that would be loaded remotely."""
|
||||||
|
found = []
|
||||||
|
cdn = re.compile(r'https://(?:unpkg\.com|cdn\.jsdelivr\.net|cdnjs\.cloudflare\.com|'
|
||||||
|
r'fonts\.googleapis\.com|fonts\.gstatic\.com)/[^\s"\'`)\\]*')
|
||||||
|
css_url = re.compile(r'url\(\s*["\']?((?:https?:)?//[^)"\']+)', re.I)
|
||||||
|
css_import = re.compile(r'@import\s+(?:url\(\s*)?["\']?((?:https?:)?//[^)"\'; ]+)', re.I)
|
||||||
|
for base, _, files in os.walk(docs):
|
||||||
|
for file in files:
|
||||||
|
path = os.path.join(base, file)
|
||||||
|
rel = os.path.relpath(path, docs)
|
||||||
|
urls = []
|
||||||
|
if file.endswith('.html'):
|
||||||
|
text = read(path)
|
||||||
|
for m in re.finditer(r'<[a-zA-Z][^>]*>', text):
|
||||||
|
tag = m.group(0)
|
||||||
|
for name in ('src', 'poster'):
|
||||||
|
v = attr(tag, name)
|
||||||
|
if v and is_remote(v):
|
||||||
|
urls.append(v)
|
||||||
|
v = attr(tag, 'srcset')
|
||||||
|
if v:
|
||||||
|
urls += [c.split()[0] for c in v.split(',') if c.strip() and is_remote(c.strip())]
|
||||||
|
if re.match(r'<link\b', tag, flags=re.I):
|
||||||
|
rel_attr = (attr(tag, 'rel') or '').lower()
|
||||||
|
v = attr(tag, 'href')
|
||||||
|
if v and is_remote(v) and re.search(r'stylesheet|icon|preload|modulepreload|manifest', rel_attr):
|
||||||
|
urls.append(v)
|
||||||
|
urls += css_url.findall(text) + css_import.findall(text)
|
||||||
|
elif file.endswith('.css'):
|
||||||
|
text = read(path)
|
||||||
|
urls += css_url.findall(text) + css_import.findall(text)
|
||||||
|
elif file.endswith('.js'):
|
||||||
|
text = read(path)
|
||||||
|
urls += cdn.findall(text)
|
||||||
|
urls += re.findall(re.escape(site_url) + r'assets/[^\s"\'`)\\]*', text)
|
||||||
|
found += [f'{rel}: {u}' for u in urls if u not in ALLOWED_REMOTE]
|
||||||
|
return found
|
||||||
|
|
||||||
|
|
||||||
|
def make_docset(site, out_dir) -> int:
|
||||||
|
entries = build_entries()
|
||||||
|
if problems:
|
||||||
|
print('\n'.join(problems), file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
docset = os.path.join(out_dir, DOCSET)
|
||||||
|
docs = os.path.join(docset, 'Contents', 'Resources', 'Documents')
|
||||||
|
if os.path.exists(docset):
|
||||||
|
shutil.rmtree(docset)
|
||||||
|
shutil.copytree(site, docs)
|
||||||
|
for icon in ('icon.png', 'icon@2x.png'):
|
||||||
|
shutil.copy(os.path.join(HERE, icon), docset)
|
||||||
|
shutil.copy(os.path.join(HERE, 'Info.plist'), os.path.join(docset, 'Contents'))
|
||||||
|
write_index(entries, os.path.join(docset, 'Contents', 'Resources', 'docSet.dsidx'))
|
||||||
|
|
||||||
|
# patch CSS to hide navigation items and fix spacing
|
||||||
|
css = glob.glob(os.path.join(docs, 'assets', 'stylesheets', 'main.*.min.css'))
|
||||||
|
if len(css) != 1:
|
||||||
|
print(f'generate_docset.py: expected exactly one main.*.min.css, found {len(css)}', file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
with open(css[0], 'a', encoding='utf-8') as f:
|
||||||
|
f.write(CSS_PATCH)
|
||||||
|
|
||||||
|
patch_titles(docs, entries)
|
||||||
|
remove_source_widget(docs)
|
||||||
|
for sitemap in glob.glob(os.path.join(docs, 'sitemap.*')):
|
||||||
|
os.remove(sitemap)
|
||||||
|
|
||||||
|
# make the docset self-contained
|
||||||
|
site_url = load_mkdocs_yml()['site_url']
|
||||||
|
localize_images(docs)
|
||||||
|
localize_site_urls(docs, site_url)
|
||||||
|
remote = remote_loads(docs, site_url)
|
||||||
|
if remote:
|
||||||
|
print('generate_docset.py: remote resources remain in the docset:', file=sys.stderr)
|
||||||
|
print('\n'.join(' ' + r for r in remote), file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def make_tgz(out_dir) -> int:
|
||||||
|
docset = os.path.join(out_dir, DOCSET)
|
||||||
|
if not os.path.isdir(docset):
|
||||||
|
print(f'generate_docset.py: {docset} does not exist', file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
with tarfile.open(os.path.join(out_dir, 'JSON_for_Modern_C++.tgz'), 'w:gz') as tar:
|
||||||
|
tar.add(docset, arcname=DOCSET, filter=lambda i: None if os.path.basename(i.name) == '.DS_Store' else i)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
sub = parser.add_subparsers(dest='command', required=True)
|
||||||
|
sub.add_parser('list', help='print the index entries as TSV')
|
||||||
|
p = sub.add_parser('index', help='write the SQLite search index')
|
||||||
|
p.add_argument('out')
|
||||||
|
p = sub.add_parser('docset', help='build the docset from a built mkdocs site')
|
||||||
|
p.add_argument('site_dir')
|
||||||
|
p.add_argument('out_dir')
|
||||||
|
p = sub.add_parser('tgz', help='pack the docset into a tarball')
|
||||||
|
p.add_argument('out_dir')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.command == 'docset':
|
||||||
|
return make_docset(args.site_dir, args.out_dir)
|
||||||
|
if args.command == 'tgz':
|
||||||
|
return make_tgz(args.out_dir)
|
||||||
|
|
||||||
|
entries = build_entries()
|
||||||
|
if problems:
|
||||||
|
print('\n'.join(problems), file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
if args.command == 'list':
|
||||||
|
for entry in entries:
|
||||||
|
print('\t'.join(entry))
|
||||||
|
elif args.command == 'index':
|
||||||
|
write_index(entries, args.out)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -17,6 +17,7 @@ build: style_check
|
|||||||
|
|
||||||
style_check:
|
style_check:
|
||||||
@cd docs ; ../venv/bin/python3 ../scripts/check_structure.py
|
@cd docs ; ../venv/bin/python3 ../scripts/check_structure.py
|
||||||
|
@venv/bin/python3 ../docset/generate_docset.py list > /dev/null
|
||||||
|
|
||||||
# check that all Mermaid diagrams parse (needs Node.js)
|
# check that all Mermaid diagrams parse (needs Node.js)
|
||||||
# This target is used in the CI (ci_test_documentation_mermaid).
|
# This target is used in the CI (ci_test_documentation_mermaid).
|
||||||
|
|||||||
@@ -119,6 +119,14 @@ changes to any JSON value.
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
!!! warning "`null` members are not missing"
|
||||||
|
|
||||||
|
The default value is used only if the key (or JSON Pointer) does not exist. A member that exists but is
|
||||||
|
`#!json null` is converted like any other value, so `#!cpp j.value("k", 0)` throws a
|
||||||
|
[`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `"k"` is `#!json null`. See
|
||||||
|
[Access with default value](../../features/element_access/default_value.md)
|
||||||
|
for alternatives.
|
||||||
|
|
||||||
!!! warning "Return type"
|
!!! warning "Return type"
|
||||||
|
|
||||||
The value function is a template, and the return type of the function is determined by the type of the provided
|
The value function is a template, and the return type of the function is determined by the type of the provided
|
||||||
|
|||||||
@@ -347,6 +347,13 @@ struct adl_serializer<boost::optional<T>> {
|
|||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! tip "`std::optional` needs no serializer"
|
||||||
|
|
||||||
|
Since version 3.12.0, `std::optional<T>` is supported out of the box when compiling with C++17
|
||||||
|
(`std::nullopt` is converted to and from `null`). Do not write an `adl_serializer` for it; this pattern is only
|
||||||
|
needed for types such as `boost::optional` or for custom semantics. See [Conversions](conversions.md) and
|
||||||
|
[Omitting a field when serializing `std::optional`](conversions.md#omitting-a-field-when-serializing-stdoptional).
|
||||||
|
|
||||||
!!! note "ABI compatibility"
|
!!! note "ABI compatibility"
|
||||||
|
|
||||||
Use [`NLOHMANN_JSON_NAMESPACE_BEGIN`](../api/macros/nlohmann_json_namespace_begin.md) and `NLOHMANN_JSON_NAMESPACE_END`
|
Use [`NLOHMANN_JSON_NAMESPACE_BEGIN`](../api/macros/nlohmann_json_namespace_begin.md) and `NLOHMANN_JSON_NAMESPACE_END`
|
||||||
|
|||||||
@@ -30,6 +30,47 @@ you want to access and a default value in case there is no value stored with tha
|
|||||||
| `#!cpp j.value("append", false)` | `#!json true` |
|
| `#!cpp j.value("append", false)` | `#!json true` |
|
||||||
| `#!cpp j.value("logLevel", "verbose")` | `#!json "verbose"` |
|
| `#!cpp j.value("logLevel", "verbose")` | `#!json "verbose"` |
|
||||||
|
|
||||||
|
## Nested values
|
||||||
|
|
||||||
|
To read a value deep inside a document, pass a [JSON Pointer](../json_pointer.md) instead of a key. The default value is
|
||||||
|
returned if the value at the pointer does not exist, including the case that an intermediate key is missing. There is
|
||||||
|
no need to check each level with [`contains`](../../api/basic_json/contains.md) first.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
json j = {{"server", {{"port", 8080}}}, {"list", {10, 20}}};
|
||||||
|
|
||||||
|
int port = j.value("/server/port"_json_pointer, 80); // 8080
|
||||||
|
int timeout = j.value("/server/limits/timeout"_json_pointer, 30); // 30 (missing intermediate key)
|
||||||
|
int second = j.value("/list/1"_json_pointer, 0); // 20 (numeric tokens index arrays)
|
||||||
|
int third = j.value("/list/5"_json_pointer, 0); // 0 (index out of range)
|
||||||
|
|
||||||
|
bool has_port = j.contains("/server/port"_json_pointer); // true
|
||||||
|
bool has_host = j.contains("/server/host/name"_json_pointer); // false
|
||||||
|
```
|
||||||
|
|
||||||
|
If the path is only available as a dotted string such as `#!cpp "server.port"`, do not build the pointer by
|
||||||
|
concatenating `#!cpp "/"` and the parts: keys containing `/` or `~` would be misinterpreted. Append each part as a
|
||||||
|
reference token with [`operator/=`](../../api/json_pointer/operator_slasheq.md) instead. It escapes the token for you.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
json::json_pointer to_pointer(const std::string& dotted)
|
||||||
|
{
|
||||||
|
json::json_pointer ptr;
|
||||||
|
std::istringstream in(dotted);
|
||||||
|
for (std::string token; std::getline(in, token, '.');)
|
||||||
|
{
|
||||||
|
ptr /= token;
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int port = j.value(to_pointer("server.port"), 80); // 8080
|
||||||
|
int first = j.value(to_pointer("list.0"), 0); // 10
|
||||||
|
```
|
||||||
|
|
||||||
|
The key `#!cpp "a/b"` yields the pointer `#!cpp "/a~1b"`; the dot-splitting itself is up to the caller, so keys
|
||||||
|
containing `.` need a different separator.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
!!! failure "Exceptions"
|
!!! failure "Exceptions"
|
||||||
@@ -37,6 +78,31 @@ you want to access and a default value in case there is no value stored with tha
|
|||||||
- With string keys, `value` can only be used with objects. For other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown.
|
- With string keys, `value` can only be used with objects. For other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown.
|
||||||
- With JSON Pointers, `value` can be used with both objects and arrays. For other types (null, boolean, number, string), a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown.
|
- With JSON Pointers, `value` can be used with both objects and arrays. For other types (null, boolean, number, string), a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown.
|
||||||
|
|
||||||
|
!!! warning "`null` and mistyped members are not missing"
|
||||||
|
|
||||||
|
`value` returns the default value only if the key is **absent**. If the member exists, it is converted to the type
|
||||||
|
of the default value, even if it is `#!json null`. For `#!json {"k": null}`, the call `#!cpp j.value("k", 0)` throws
|
||||||
|
a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error302), and so does a member of another
|
||||||
|
type such as a string where a number is expected. The same holds for JSON Pointers.
|
||||||
|
|
||||||
|
To treat `#!json null` like a missing value, check for it explicitly:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int n = (j.contains("k") && !j["k"].is_null()) ? j["k"].get<int>() : 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
With C++17, [`get<std::optional<T>>()`](../../api/basic_json/get.md) maps `#!json null` to an empty optional. As
|
||||||
|
[`at`](../../api/basic_json/at.md) throws [`out_of_range`](../../home/exceptions.md#jsonexceptionout_of_range403)
|
||||||
|
for an absent key, use [`find`](../../api/basic_json/find.md) to cover both cases:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::optional<int> n; // empty if "k" is absent or null
|
||||||
|
if (const auto it = j.find("k"); it != j.end())
|
||||||
|
{
|
||||||
|
n = it->get<std::optional<int>>(); // still throws for a non-number such as "text"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
!!! warning "Return type"
|
!!! warning "Return type"
|
||||||
|
|
||||||
The value function is a template, and the return type of the function is determined by the type of the provided
|
The value function is a template, and the return type of the function is determined by the type of the provided
|
||||||
@@ -63,3 +129,6 @@ you want to access and a default value in case there is no value stored with tha
|
|||||||
|
|
||||||
- [`value`](../../api/basic_json/value.md) for access with default value
|
- [`value`](../../api/basic_json/value.md) for access with default value
|
||||||
- documentation on [checked access](checked_access.md)
|
- documentation on [checked access](checked_access.md)
|
||||||
|
- documentation on [JSON Pointer](../json_pointer.md)
|
||||||
|
- [`contains`](../../api/basic_json/contains.md) to check whether a key or JSON Pointer exists
|
||||||
|
- [`json_pointer::operator/=`](../../api/json_pointer/operator_slasheq.md) to build a pointer token by token
|
||||||
@@ -77,6 +77,10 @@ auto val2 = j.at(json::json_pointer("/nested/three/1")); // false
|
|||||||
auto val3 = j.value(json::json_pointer("/nested/four"), 0); // 0
|
auto val3 = j.value(json::json_pointer("/nested/four"), 0); // 0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To read a value with a fallback, use [`value`](../api/basic_json/value.md) with a JSON Pointer; to test for existence,
|
||||||
|
use [`contains`](../api/basic_json/contains.md). Neither needs intermediate checks, see
|
||||||
|
[Nested values](element_access/default_value.md#nested-values).
|
||||||
|
|
||||||
!!! note "Creating intermediate levels that don't exist"
|
!!! note "Creating intermediate levels that don't exist"
|
||||||
|
|
||||||
See the [`operator[]` notes](../api/basic_json/operator%5B%5D.md#return-value) for how array vs. object is
|
See the [`operator[]` notes](../api/basic_json/operator%5B%5D.md#return-value) for how array vs. object is
|
||||||
@@ -126,6 +130,8 @@ auto j_original = j_flat.unflatten();
|
|||||||
## See also
|
## See also
|
||||||
|
|
||||||
- Class [`json_pointer`](../api/json_pointer/index.md)
|
- Class [`json_pointer`](../api/json_pointer/index.md)
|
||||||
|
- Functions [`value`](../api/basic_json/value.md), [`contains`](../api/basic_json/contains.md), and
|
||||||
|
[`at`](../api/basic_json/at.md) accept JSON Pointers; see [Nested values](element_access/default_value.md#nested-values)
|
||||||
- Function [`flatten`](../api/basic_json/flatten.md)
|
- Function [`flatten`](../api/basic_json/flatten.md)
|
||||||
- Function [`unflatten`](../api/basic_json/unflatten.md)
|
- Function [`unflatten`](../api/basic_json/unflatten.md)
|
||||||
- [JSON Patch](json_patch.md) - paths inside a patch are JSON Pointers
|
- [JSON Patch](json_patch.md) - paths inside a patch are JSON Pointers
|
||||||
|
|||||||
@@ -9,6 +9,13 @@ syntax that closely mimics the document being modified. Unlike [JSON Patch](json
|
|||||||
express every kind of change (e.g., it cannot reorder array elements or remove a specific array element), but it is
|
express every kind of change (e.g., it cannot reorder array elements or remove a specific array element), but it is
|
||||||
easier to read and write for object-shaped documents.
|
easier to read and write for object-shaped documents.
|
||||||
|
|
||||||
|
!!! tip "Not a general deep merge"
|
||||||
|
|
||||||
|
A merge patch is not a general deep merge: a `#!json null` value in the patch deletes the key from the target.
|
||||||
|
To merge two objects recursively (e.g., defaults and user settings), use
|
||||||
|
[`update`](../api/basic_json/update.md) with `merge_objects` set to `#!cpp true`; see
|
||||||
|
[Merging objects](modifying_values.md#merging-objects).
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
The following code shows how a JSON Merge Patch is applied to a JSON document.
|
The following code shows how a JSON Merge Patch is applied to a JSON document.
|
||||||
@@ -28,3 +35,4 @@ easier to read and write for object-shaped documents.
|
|||||||
- [JSON Patch and Diff](json_patch.md) - a more expressive alternative that describes a sequence of operations
|
- [JSON Patch and Diff](json_patch.md) - a more expressive alternative that describes a sequence of operations
|
||||||
- [JSON Pointer](json_pointer.md) - the addressing scheme used by JSON Patch
|
- [JSON Pointer](json_pointer.md) - the addressing scheme used by JSON Patch
|
||||||
- Function [`merge_patch`](../api/basic_json/merge_patch.md)
|
- Function [`merge_patch`](../api/basic_json/merge_patch.md)
|
||||||
|
- Function [`update`](../api/basic_json/update.md) - merge objects, optionally recursively
|
||||||
@@ -35,8 +35,12 @@ the insertion happened — useful for "add if absent" semantics.
|
|||||||
|
|
||||||
## Merging objects
|
## Merging objects
|
||||||
|
|
||||||
To merge one object into another, [`update`](../api/basic_json/update.md) copies all members from another object,
|
To merge one object into another, [`update`](../api/basic_json/update.md) copies all members from another object
|
||||||
overwriting existing keys (similar to Python's `dict.update`). This is the idiomatic way to combine two objects.
|
(similar to Python's `dict.update`). This is the idiomatic way to combine two objects. It has two modes:
|
||||||
|
|
||||||
|
- By default, the merge is shallow: existing keys are overwritten, even if both values are objects.
|
||||||
|
- With `merge_objects = #!cpp true`, keys whose values are objects in both JSON values are merged recursively.
|
||||||
|
Everything else is overwritten. In particular, arrays are replaced, not concatenated.
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
@@ -50,9 +54,22 @@ overwriting existing keys (similar to Python's `dict.update`). This is the idiom
|
|||||||
--8<-- "examples/update.output"
|
--8<-- "examples/update.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
For a recursive merge that follows [RFC 7386](https://tools.ietf.org/html/rfc7386), see
|
A common use of the recursive mode is combining defaults with user settings. Nested defaults that the user did not set
|
||||||
[JSON Merge Patch](merge_patch.md). To apply a sequence of well-defined edit operations, see
|
are kept:
|
||||||
[JSON Patch](json_patch.md).
|
|
||||||
|
```cpp
|
||||||
|
json defaults = {{"log", {{"level", "info"}, {"file", "app.log"}}}, {"retries", 3}};
|
||||||
|
json user_settings = {{"log", {{"level", "debug"}}}};
|
||||||
|
|
||||||
|
json config = defaults;
|
||||||
|
config.update(user_settings, true);
|
||||||
|
// {"log":{"file":"app.log","level":"debug"},"retries":3}
|
||||||
|
```
|
||||||
|
|
||||||
|
[JSON Merge Patch](merge_patch.md) ([RFC 7386](https://tools.ietf.org/html/rfc7386)) also merges objects recursively,
|
||||||
|
but it is a different tool: a `#!json null` in the patch means "remove this key". It is meant for applying merge patch
|
||||||
|
documents (e.g., received via HTTP PATCH). To merge configuration-like objects, use `#!cpp update(..., true)`. To apply
|
||||||
|
a sequence of well-defined edit operations, see [JSON Patch](json_patch.md).
|
||||||
|
|
||||||
## Removing elements
|
## Removing elements
|
||||||
|
|
||||||
@@ -72,6 +89,7 @@ a.erase(1); // [1,3,4] (erase by index)
|
|||||||
|
|
||||||
- [`push_back`](../api/basic_json/push_back.md) / [`emplace_back`](../api/basic_json/emplace_back.md) - append to an array
|
- [`push_back`](../api/basic_json/push_back.md) / [`emplace_back`](../api/basic_json/emplace_back.md) - append to an array
|
||||||
- [`emplace`](../api/basic_json/emplace.md) - insert into an object if the key is absent
|
- [`emplace`](../api/basic_json/emplace.md) - insert into an object if the key is absent
|
||||||
- [`update`](../api/basic_json/update.md) - merge objects
|
- [`update`](../api/basic_json/update.md) - merge objects (shallow, or recursive with `merge_objects`)
|
||||||
|
- [`merge_patch`](../api/basic_json/merge_patch.md) - apply an RFC 7386 merge patch
|
||||||
- [`erase`](../api/basic_json/erase.md) / [`clear`](../api/basic_json/clear.md) - remove elements
|
- [`erase`](../api/basic_json/erase.md) / [`clear`](../api/basic_json/clear.md) - remove elements
|
||||||
- [JSON Patch and Diff](json_patch.md) and [JSON Merge Patch](merge_patch.md) - structured modifications
|
- [JSON Patch and Diff](json_patch.md) and [JSON Merge Patch](merge_patch.md) - structured modifications
|
||||||
@@ -288,36 +288,6 @@ def check_header_links() -> None:
|
|||||||
f'link to "{match.group(0)}" does not point to a documentation page')
|
f'link to "{match.group(0)}" does not point to a documentation page')
|
||||||
|
|
||||||
|
|
||||||
def check_docset() -> None:
|
|
||||||
"""Every API page and every macro has an entry in the docset index; no entry points to a missing page."""
|
|
||||||
entry_re = re.compile(r"VALUES \('((?:[^']|'')*)', '(\w+)', '([^']*)'\);")
|
|
||||||
names_by_path = {}
|
|
||||||
with open("../../docset/docSet.sql", encoding="utf-8") as sql:
|
|
||||||
for name, _, path in entry_re.findall(sql.read()):
|
|
||||||
names_by_path.setdefault(path, set()).add(name.replace("''", "'"))
|
|
||||||
|
|
||||||
def to_path(page):
|
|
||||||
if os.path.basename(page) == "index.md":
|
|
||||||
return page[:-len("index.md")] + "index.html"
|
|
||||||
return page[:-len(".md")] + "/index.html"
|
|
||||||
|
|
||||||
pages = sorted(glob.glob("**/*.md", recursive=True))
|
|
||||||
for path in sorted(set(names_by_path) - {to_path(p) for p in pages}):
|
|
||||||
report("docset/stale_entry", "../../docset/docSet.sql", f'entry "{path}" has no documentation page')
|
|
||||||
for page in (p for p in pages if p.startswith("api/")):
|
|
||||||
names = names_by_path.get(to_path(page))
|
|
||||||
if not names:
|
|
||||||
report("docset/missing_entry", page, "page has no entry in docs/docset/docSet.sql")
|
|
||||||
elif page.startswith("api/macros/") and os.path.basename(page) != "index.md":
|
|
||||||
with open(page, encoding="utf-8") as content:
|
|
||||||
text = content.read()
|
|
||||||
match = re.search(r"^# (.+)$", text, re.MULTILINE) or re.search(r"<h1>(.*?)</h1>", text, re.DOTALL)
|
|
||||||
title = re.sub(r"<[^>]+>|\s+", " ", match.group(1))
|
|
||||||
for macro in filter(None, (x.strip() for x in re.split(r"[,/]", title))):
|
|
||||||
if macro not in names:
|
|
||||||
report("docset/missing_macro", page, f'macro "{macro}" has no entry in docs/docset/docSet.sql')
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print(120 * "-")
|
print(120 * "-")
|
||||||
check_structure()
|
check_structure()
|
||||||
@@ -327,7 +297,6 @@ if __name__ == "__main__":
|
|||||||
check_heading_levels()
|
check_heading_levels()
|
||||||
check_image_alt_text()
|
check_image_alt_text()
|
||||||
check_header_links()
|
check_header_links()
|
||||||
check_docset()
|
|
||||||
print(120 * "-")
|
print(120 * "-")
|
||||||
|
|
||||||
if warnings > 0:
|
if warnings > 0:
|
||||||
|
|||||||
Reference in new issue
Block a user