mirror of
https://github.com/open-source-parsers/jsoncpp.git
synced 2025-10-15 23:20:05 +02:00
Compare commits
117 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e7233bf056 | ||
![]() |
9c90456890 | ||
![]() |
f4be815c86 | ||
![]() |
aa13a8ba40 | ||
![]() |
da0fcfbaa2 | ||
![]() |
3ebba5cea8 | ||
![]() |
acbf4eb2ef | ||
![]() |
56df206847 | ||
![]() |
4dca80da49 | ||
![]() |
249ad9f47f | ||
![]() |
99b8e856f6 | ||
![]() |
89b72e1653 | ||
![]() |
2474989f24 | ||
![]() |
315b8c9f2c | ||
![]() |
29501c4d9f | ||
![]() |
7796f20eab | ||
![]() |
20d09676c2 | ||
![]() |
5a744708fc | ||
![]() |
07f0e9308d | ||
![]() |
052050df07 | ||
![]() |
435d2a2f8d | ||
![]() |
6123bd1505 | ||
![]() |
7477bcfa3a | ||
![]() |
5e3e68af2e | ||
![]() |
04a607d95b | ||
![]() |
db75cdf21e | ||
![]() |
c41609b9f9 | ||
![]() |
b56381a636 | ||
![]() |
f757c18ca0 | ||
![]() |
3cf9175bde | ||
![]() |
a9e1ab302d | ||
![]() |
694dbcb328 | ||
![]() |
732abb80ef | ||
![]() |
f3b3358a0e | ||
![]() |
1357cddf1e | ||
![]() |
8df98f6112 | ||
![]() |
16bdfd8af3 | ||
![]() |
ce799b3aa3 | ||
![]() |
3a65581b20 | ||
![]() |
6451412c99 | ||
![]() |
66a8ba255f | ||
![]() |
249fd18114 | ||
![]() |
a587d04f77 | ||
![]() |
2c1197c2c8 | ||
![]() |
2a94618589 | ||
![]() |
dee4602b8f | ||
![]() |
ea2d167a38 | ||
![]() |
41edda5ebe | ||
![]() |
2941cb3fe2 | ||
![]() |
636121485c | ||
![]() |
fe855fb4dd | ||
![]() |
198cc350c5 | ||
![]() |
5e8595c0e2 | ||
![]() |
38042b3892 | ||
![]() |
3b5f2b85ca | ||
![]() |
7eca3b4e88 | ||
![]() |
999f5912f0 | ||
![]() |
472d29f57b | ||
![]() |
6065a1c142 | ||
![]() |
28a20917b0 | ||
![]() |
177b7b8f22 | ||
![]() |
9da9f84903 | ||
![]() |
54b8e6939a | ||
![]() |
c7b39c2e25 | ||
![]() |
d78caa3851 | ||
![]() |
c6e0688e5a | ||
![]() |
1e21e63853 | ||
![]() |
dea6f8d9a6 | ||
![]() |
648843d148 | ||
![]() |
fe3979cd8a | ||
![]() |
94665eab72 | ||
![]() |
9e4bcf354f | ||
![]() |
9243d602fe | ||
![]() |
beb6f35c63 | ||
![]() |
ceef7f5219 | ||
![]() |
77ce057f14 | ||
![]() |
d49ab5aee1 | ||
![]() |
4d649402b0 | ||
![]() |
489707ff60 | ||
![]() |
5fbfe3cdb9 | ||
![]() |
948f29032e | ||
![]() |
964affd333 | ||
![]() |
c038e08efc | ||
![]() |
74c2d82e19 | ||
![]() |
30726082f3 | ||
![]() |
1e3149ab75 | ||
![]() |
7312b1022d | ||
![]() |
2f046b584d | ||
![]() |
dd91914b1b | ||
![]() |
2a46e295ec | ||
![]() |
f4bc0bf4ec | ||
![]() |
f357688893 | ||
![]() |
bb0c80b3e5 | ||
![]() |
ff5abe76a5 | ||
![]() |
9cc0bb80b2 | ||
![]() |
494950a63d | ||
![]() |
7d82b14726 | ||
![]() |
2bc6137ada | ||
![]() |
201904bfbb | ||
![]() |
216ecd3085 | ||
![]() |
8d15e51228 | ||
![]() |
9fbd12b27c | ||
![]() |
f8ca6cbb25 | ||
![]() |
d383056fbb | ||
![]() |
ddb4ff7dec | ||
![]() |
3efc587fba | ||
![]() |
70704b9a70 | ||
![]() |
ac6bbbc739 | ||
![]() |
26c52861b9 | ||
![]() |
3682f60927 | ||
![]() |
58c31ac550 | ||
![]() |
08cfd02d8c | ||
![]() |
79211e1aeb | ||
![]() |
632c9b5032 | ||
![]() |
05810a7607 | ||
![]() |
942e2c999a | ||
![]() |
2160c9a042 |
@@ -7,12 +7,11 @@ language: cpp
|
|||||||
compiler:
|
compiler:
|
||||||
- gcc
|
- gcc
|
||||||
- clang
|
- clang
|
||||||
script: cmake -DJSONCPP_LIB_BUILD_SHARED=$SHARED_LIBRARY -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_VERBOSE_MAKEFILE=$VERBOSE_MAKE . && make
|
script: cmake -DJSONCPP_WITH_CMAKE_PACKAGE=$CMAKE_PKG -DJSONCPP_LIB_BUILD_SHARED=$SHARED_LIB -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_VERBOSE_MAKEFILE=$VERBOSE_MAKE . && make
|
||||||
env:
|
env:
|
||||||
matrix:
|
matrix:
|
||||||
- SHARED_LIBRARY=ON BUILD_TYPE=release VERBOSE_MAKE=false
|
- SHARED_LIB=ON STATIC_LIB=ON CMAKE_PKG=ON BUILD_TYPE=release VERBOSE_MAKE=false
|
||||||
- SHARED_LIBRARY=OFF BUILD_TYPE=release VERBOSE_MAKE=false
|
- SHARED_LIB=OFF STATIC_LIB=ON CMAKE_PKG=OFF BUILD_TYPE=debug VERBOSE_MAKE=true VERBOSE
|
||||||
- SHARED_LIBRARY=OFF BUILD_TYPE=debug VERBOSE VERBOSE_MAKE=true
|
|
||||||
notifications:
|
notifications:
|
||||||
email:
|
email:
|
||||||
- aaronjjacobs@gmail.com
|
- aaronjjacobs@gmail.com
|
||||||
|
@@ -85,10 +85,10 @@ endif( MSVC )
|
|||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
# using regular Clang or AppleClang
|
# using regular Clang or AppleClang
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
# using GCC
|
# using GCC
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++0x")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall -Wextra -pedantic")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
IF(JSONCPP_WITH_WARNING_AS_ERROR)
|
IF(JSONCPP_WITH_WARNING_AS_ERROR)
|
||||||
|
2
NEWS.txt
2
NEWS.txt
@@ -80,7 +80,7 @@ New in SVN
|
|||||||
(e.g. MSVC 2008 command prompt in start menu) before running scons.
|
(e.g. MSVC 2008 command prompt in start menu) before running scons.
|
||||||
|
|
||||||
- Added support for amalgamated source and header generation (a la sqlite).
|
- Added support for amalgamated source and header generation (a la sqlite).
|
||||||
Refer to README.txt section "Generating amalgamated source and header"
|
Refer to README.md section "Generating amalgamated source and header"
|
||||||
for detail.
|
for detail.
|
||||||
|
|
||||||
* Value
|
* Value
|
||||||
|
44
README.md
44
README.md
@@ -7,17 +7,20 @@ pairs.
|
|||||||
|
|
||||||
[json-org]: http://json.org/
|
[json-org]: http://json.org/
|
||||||
|
|
||||||
JsonCpp is a C++ library that allows manipulating JSON values, including
|
[JsonCpp][] is a C++ library that allows manipulating JSON values, including
|
||||||
serialization and deserialization to and from strings. It can also preserve
|
serialization and deserialization to and from strings. It can also preserve
|
||||||
existing comment in unserialization/serialization steps, making it a convenient
|
existing comment in unserialization/serialization steps, making it a convenient
|
||||||
format to store user input files.
|
format to store user input files.
|
||||||
|
|
||||||
|
[JsonCpp]: http://open-source-parsers.github.io/jsoncpp-docs/doxygen/index.html
|
||||||
|
|
||||||
## A note on backward-compatibility
|
## A note on backward-compatibility
|
||||||
Very soon, we are switching to C++11 only. For older compilers, try the `pre-C++11` branch.
|
* `1.y.z` is built with C++11.
|
||||||
|
* `0.8.z` can be used with older compilers.
|
||||||
|
* Major versions maintain binary-compatibility.
|
||||||
|
|
||||||
Using JsonCpp in your project
|
Using JsonCpp in your project
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
The recommended approach to integrating JsonCpp in your project is to build
|
The recommended approach to integrating JsonCpp in your project is to build
|
||||||
the amalgamated source (a single `.cpp` file) with your own build system. This
|
the amalgamated source (a single `.cpp` file) with your own build system. This
|
||||||
ensures consistency of compilation flags and ABI compatibility. See the section
|
ensures consistency of compilation flags and ABI compatibility. See the section
|
||||||
@@ -28,13 +31,11 @@ should be included as follow:
|
|||||||
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
If JsonCpp was build as a dynamic library on Windows, then your project needs to
|
If JsonCpp was built as a dynamic library on Windows, then your project needs to
|
||||||
define the macro `JSON_DLL`.
|
define the macro `JSON_DLL`.
|
||||||
|
|
||||||
|
Building and testing with CMake
|
||||||
Building and testing with new CMake
|
-------------------------------
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
[CMake][] is a C++ Makefiles/Solution generator. It is usually available on most
|
[CMake][] is a C++ Makefiles/Solution generator. It is usually available on most
|
||||||
Linux system as package. On Ubuntu:
|
Linux system as package. On Ubuntu:
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ Alternatively, from the command-line on Unix in the source directory:
|
|||||||
|
|
||||||
mkdir -p build/debug
|
mkdir -p build/debug
|
||||||
cd build/debug
|
cd build/debug
|
||||||
cmake -DCMAKE_BUILD_TYPE=debug -DJSONCPP_LIB_BUILD_SHARED=OFF -G "Unix Makefiles" ../..
|
cmake -DCMAKE_BUILD_TYPE=debug -DJSONCPP_LIB_BUILD_STATIC=ON -DJSONCPP_LIB_BUILD_SHARED=OFF -G "Unix Makefiles" ../..
|
||||||
make
|
make
|
||||||
|
|
||||||
Running `cmake -`" will display the list of available generators (passed using
|
Running `cmake -`" will display the list of available generators (passed using
|
||||||
@@ -75,10 +76,8 @@ the `-G` option).
|
|||||||
By default CMake hides compilation commands. This can be modified by specifying
|
By default CMake hides compilation commands. This can be modified by specifying
|
||||||
`-DCMAKE_VERBOSE_MAKEFILE=true` when generating makefiles.
|
`-DCMAKE_VERBOSE_MAKEFILE=true` when generating makefiles.
|
||||||
|
|
||||||
|
|
||||||
Building and testing with SCons
|
Building and testing with SCons
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
**Note:** The SCons-based build system is deprecated. Please use CMake; see the
|
**Note:** The SCons-based build system is deprecated. Please use CMake; see the
|
||||||
section above.
|
section above.
|
||||||
|
|
||||||
@@ -107,14 +106,7 @@ If you are building with Microsoft Visual Studio 2008, you need to set up the
|
|||||||
environment by running `vcvars32.bat` (e.g. MSVC 2008 command prompt) before
|
environment by running `vcvars32.bat` (e.g. MSVC 2008 command prompt) before
|
||||||
running SCons.
|
running SCons.
|
||||||
|
|
||||||
|
# Running the tests manually
|
||||||
Running the tests manually
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Note that test can be run using SCons using the `check` target:
|
|
||||||
|
|
||||||
scons platform=$PLATFORM check
|
|
||||||
|
|
||||||
You need to run tests manually only if you are troubleshooting an issue.
|
You need to run tests manually only if you are troubleshooting an issue.
|
||||||
|
|
||||||
In the instructions below, replace `path/to/jsontest` with the path of the
|
In the instructions below, replace `path/to/jsontest` with the path of the
|
||||||
@@ -137,20 +129,21 @@ In the instructions below, replace `path/to/jsontest` with the path of the
|
|||||||
# You can run the tests using valgrind:
|
# You can run the tests using valgrind:
|
||||||
python rununittests.py --valgrind path/to/test_lib_json
|
python rununittests.py --valgrind path/to/test_lib_json
|
||||||
|
|
||||||
|
## Running the tests using scons
|
||||||
|
Note that tests can be run using SCons using the `check` target:
|
||||||
|
|
||||||
|
scons platform=$PLATFORM check
|
||||||
|
|
||||||
Building the documentation
|
Building the documentation
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
Run the Python script `doxybuild.py` from the top directory:
|
Run the Python script `doxybuild.py` from the top directory:
|
||||||
|
|
||||||
python doxybuild.py --doxygen=$(which doxygen) --open --with-dot
|
python doxybuild.py --doxygen=$(which doxygen) --open --with-dot
|
||||||
|
|
||||||
See `doxybuild.py --help` for options.
|
See `doxybuild.py --help` for options.
|
||||||
|
|
||||||
|
|
||||||
Generating amalgamated source and header
|
Generating amalgamated source and header
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
JsonCpp is provided with a script to generate a single header and a single
|
JsonCpp is provided with a script to generate a single header and a single
|
||||||
source file to ease inclusion into an existing project. The amalgamated source
|
source file to ease inclusion into an existing project. The amalgamated source
|
||||||
can be generated at any time by running the following command from the
|
can be generated at any time by running the following command from the
|
||||||
@@ -172,10 +165,8 @@ The amalgamated sources are generated by concatenating JsonCpp source in the
|
|||||||
correct order and defining the macro `JSON_IS_AMALGAMATION` to prevent inclusion
|
correct order and defining the macro `JSON_IS_AMALGAMATION` to prevent inclusion
|
||||||
of other headers.
|
of other headers.
|
||||||
|
|
||||||
|
|
||||||
Adding a reader/writer test
|
Adding a reader/writer test
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
To add a test, you need to create two files in test/data:
|
To add a test, you need to create two files in test/data:
|
||||||
|
|
||||||
* a `TESTNAME.json` file, that contains the input document in JSON format.
|
* a `TESTNAME.json` file, that contains the input document in JSON format.
|
||||||
@@ -195,10 +186,8 @@ The `TESTNAME.expected` file format is as follows:
|
|||||||
See the examples `test_complex_01.json` and `test_complex_01.expected` to better
|
See the examples `test_complex_01.json` and `test_complex_01.expected` to better
|
||||||
understand element paths.
|
understand element paths.
|
||||||
|
|
||||||
|
|
||||||
Understanding reader/writer test output
|
Understanding reader/writer test output
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
When a test is run, output files are generated beside the input test files.
|
When a test is run, output files are generated beside the input test files.
|
||||||
Below is a short description of the content of each file:
|
Below is a short description of the content of each file:
|
||||||
|
|
||||||
@@ -215,10 +204,7 @@ Below is a short description of the content of each file:
|
|||||||
* `test_complex_01.process-output`: `jsontest` output, typically useful for
|
* `test_complex_01.process-output`: `jsontest` output, typically useful for
|
||||||
understanding parsing errors.
|
understanding parsing errors.
|
||||||
|
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
See the `LICENSE` file for details. In summary, JsonCpp is licensed under the
|
See the `LICENSE` file for details. In summary, JsonCpp is licensed under the
|
||||||
MIT license, or public domain if desired and recognized in your jurisdiction.
|
MIT license, or public domain if desired and recognized in your jurisdiction.
|
||||||
|
|
||||||
|
@@ -237,7 +237,7 @@ RunUnitTests = ActionFactory(runUnitTests_action, runUnitTests_string )
|
|||||||
env.Alias( 'check' )
|
env.Alias( 'check' )
|
||||||
|
|
||||||
srcdist_cmd = env['SRCDIST_ADD']( source = """
|
srcdist_cmd = env['SRCDIST_ADD']( source = """
|
||||||
AUTHORS README.txt SConstruct
|
AUTHORS README.md SConstruct
|
||||||
""".split() )
|
""".split() )
|
||||||
env.Alias( 'src-dist', srcdist_cmd )
|
env.Alias( 'src-dist', srcdist_cmd )
|
||||||
|
|
||||||
|
@@ -211,18 +211,15 @@ def generate_html_report( html_report_path, builds ):
|
|||||||
build_status = 'ok' if build.build_succeeded else 'FAILED'
|
build_status = 'ok' if build.build_succeeded else 'FAILED'
|
||||||
cmake_log_url = os.path.relpath(build.cmake_log_path, report_dir)
|
cmake_log_url = os.path.relpath(build.cmake_log_path, report_dir)
|
||||||
build_log_url = os.path.relpath(build.build_log_path, report_dir)
|
build_log_url = os.path.relpath(build.build_log_path, report_dir)
|
||||||
td = '<td class="%s"><a href="%s" class="%s">CMake: %s</a>' % (
|
td = '<td class="%s"><a href="%s" class="%s">CMake: %s</a>' % ( build_status.lower(), cmake_log_url, cmake_status.lower(), cmake_status)
|
||||||
build_status.lower(), cmake_log_url, cmake_status.lower(), cmake_status)
|
|
||||||
if build.cmake_succeeded:
|
if build.cmake_succeeded:
|
||||||
td += '<br><a href="%s" class="%s">Build: %s</a>' % (
|
td += '<br><a href="%s" class="%s">Build: %s</a>' % ( build_log_url, build_status.lower(), build_status)
|
||||||
build_log_url, build_status.lower(), build_status)
|
|
||||||
td += '</td>'
|
td += '</td>'
|
||||||
else:
|
else:
|
||||||
td = '<td></td>'
|
td = '<td></td>'
|
||||||
tds.append(td)
|
tds.append(td)
|
||||||
tr_builds.append('<tr>%s</tr>' % '\n'.join(tds))
|
tr_builds.append('<tr>%s</tr>' % '\n'.join(tds))
|
||||||
html = HTML_TEMPLATE.substitute(
|
html = HTML_TEMPLATE.substitute( title='Batch build report',
|
||||||
title='Batch build report',
|
|
||||||
th_vars=' '.join(th_vars),
|
th_vars=' '.join(th_vars),
|
||||||
th_build_types=' '.join(th_build_types),
|
th_build_types=' '.join(th_build_types),
|
||||||
tr_builds='\n'.join(tr_builds))
|
tr_builds='\n'.join(tr_builds))
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import os.path
|
from contextlib import closing
|
||||||
import gzip
|
import os
|
||||||
import tarfile
|
import tarfile
|
||||||
|
|
||||||
TARGZ_DEFAULT_COMPRESSION_LEVEL = 9
|
TARGZ_DEFAULT_COMPRESSION_LEVEL = 9
|
||||||
@@ -29,25 +29,19 @@ def make_tarball(tarball_path, sources, base_dir, prefix_dir=''):
|
|||||||
path_in_tar = archive_name(path)
|
path_in_tar = archive_name(path)
|
||||||
tar.add(path, path_in_tar)
|
tar.add(path, path_in_tar)
|
||||||
compression = TARGZ_DEFAULT_COMPRESSION_LEVEL
|
compression = TARGZ_DEFAULT_COMPRESSION_LEVEL
|
||||||
tar = tarfile.TarFile.gzopen( tarball_path, 'w', compresslevel=compression )
|
with closing(tarfile.TarFile.open(tarball_path, 'w:gz',
|
||||||
try:
|
compresslevel=compression)) as tar:
|
||||||
for source in sources:
|
for source in sources:
|
||||||
source_path = source
|
source_path = source
|
||||||
if os.path.isdir(source):
|
if os.path.isdir(source):
|
||||||
os.path.walk(source_path, visit, tar)
|
for dirpath, dirnames, filenames in os.walk(source_path):
|
||||||
|
visit(tar, dirpath, filenames)
|
||||||
else:
|
else:
|
||||||
path_in_tar = archive_name(source_path)
|
path_in_tar = archive_name(source_path)
|
||||||
tar.add(source_path, path_in_tar) # filename, arcname
|
tar.add(source_path, path_in_tar) # filename, arcname
|
||||||
finally:
|
|
||||||
tar.close()
|
|
||||||
|
|
||||||
def decompress(tarball_path, base_dir):
|
def decompress(tarball_path, base_dir):
|
||||||
"""Decompress the gzipped tarball into directory base_dir.
|
"""Decompress the gzipped tarball into directory base_dir.
|
||||||
"""
|
"""
|
||||||
# !!! This class method is not documented in the online doc
|
with closing(tarfile.TarFile.open(tarball_path)) as tar:
|
||||||
# nor is bz2open!
|
|
||||||
tar = tarfile.TarFile.gzopen(tarball_path, mode='r')
|
|
||||||
try:
|
|
||||||
tar.extractall(base_dir)
|
tar.extractall(base_dir)
|
||||||
finally:
|
|
||||||
tar.close()
|
|
||||||
|
@@ -819,7 +819,7 @@ EXCLUDE_SYMBOLS =
|
|||||||
# that contain example code fragments that are included (see the \include
|
# that contain example code fragments that are included (see the \include
|
||||||
# command).
|
# command).
|
||||||
|
|
||||||
EXAMPLE_PATH =
|
EXAMPLE_PATH = ..
|
||||||
|
|
||||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||||
|
@@ -16,7 +16,7 @@ JsonCpp - JSON data format manipulation library
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td width="40%" align="right" valign="center">
|
<td width="40%" align="right" valign="center">
|
||||||
<a href="https://github.com/open-source-parsers/jsoncpp">JsonCpp home page</a>
|
<a href="http://open-source-parsers.github.io/jsoncpp-docs/doxygen/">JsonCpp home page</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
133
doc/jsoncpp.dox
133
doc/jsoncpp.dox
@@ -4,11 +4,21 @@
|
|||||||
|
|
||||||
<a HREF="http://www.json.org/">JSON (JavaScript Object Notation)</a>
|
<a HREF="http://www.json.org/">JSON (JavaScript Object Notation)</a>
|
||||||
is a lightweight data-interchange format.
|
is a lightweight data-interchange format.
|
||||||
It can represent integer, real number, string, an ordered sequence of value, and
|
|
||||||
a collection of name/value pairs.
|
|
||||||
|
|
||||||
Here is an example of JSON data:
|
Here is an example of JSON data:
|
||||||
\verbatim
|
\verbatim
|
||||||
|
{
|
||||||
|
"encoding" : "UTF-8",
|
||||||
|
"plug-ins" : [
|
||||||
|
"python",
|
||||||
|
"c++",
|
||||||
|
"ruby"
|
||||||
|
],
|
||||||
|
"indent" : { "length" : 3, "use_space": true }
|
||||||
|
}
|
||||||
|
\endverbatim
|
||||||
|
<b>JsonCpp</b> supports comments as <i>meta-data</i>:
|
||||||
|
\code
|
||||||
// Configuration options
|
// Configuration options
|
||||||
{
|
{
|
||||||
// Default encoding for text
|
// Default encoding for text
|
||||||
@@ -17,22 +27,22 @@ Here is an example of JSON data:
|
|||||||
// Plug-ins loaded at start-up
|
// Plug-ins loaded at start-up
|
||||||
"plug-ins" : [
|
"plug-ins" : [
|
||||||
"python",
|
"python",
|
||||||
"c++",
|
"c++", // trailing comment
|
||||||
"ruby"
|
"ruby"
|
||||||
],
|
],
|
||||||
|
|
||||||
// Tab indent size
|
// Tab indent size
|
||||||
"indent" : { "length" : 3, "use_space": true }
|
// (multi-line comment)
|
||||||
|
"indent" : { /*embedded comment*/ "length" : 3, "use_space": true }
|
||||||
}
|
}
|
||||||
\endverbatim
|
\endcode
|
||||||
<code>jsoncpp</code> supports comments as <i>meta-data</i>.
|
|
||||||
|
|
||||||
\section _features Features
|
\section _features Features
|
||||||
- read and write JSON document
|
- read and write JSON document
|
||||||
- attach C++ style comments to element during parsing
|
- attach C++ style comments to element during parsing
|
||||||
- rewrite JSON document preserving original comments
|
- rewrite JSON document preserving original comments
|
||||||
|
|
||||||
Notes: Comments used to be supported in JSON but where removed for
|
Notes: Comments used to be supported in JSON but were removed for
|
||||||
portability (C like comments are not supported in Python). Since
|
portability (C like comments are not supported in Python). Since
|
||||||
comments are useful in configuration/input file, this feature was
|
comments are useful in configuration/input file, this feature was
|
||||||
preserved.
|
preserved.
|
||||||
@@ -40,47 +50,77 @@ preserved.
|
|||||||
\section _example Code example
|
\section _example Code example
|
||||||
|
|
||||||
\code
|
\code
|
||||||
Json::Value root; // will contains the root value after parsing.
|
Json::Value root; // 'root' will contain the root value after parsing.
|
||||||
Json::Reader reader;
|
std::cin >> root;
|
||||||
bool parsingSuccessful = reader.parse( config_doc, root );
|
|
||||||
if ( !parsingSuccessful )
|
|
||||||
{
|
|
||||||
// report to the user the failure and their locations in the document.
|
|
||||||
std::cout << "Failed to parse configuration\n"
|
|
||||||
<< reader.getFormattedErrorMessages();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the value of the member of root named 'encoding', return 'UTF-8' if there is no
|
// You can also read into a particular sub-value.
|
||||||
// such member.
|
|
||||||
std::string encoding = root.get("encoding", "UTF-8" ).asString();
|
|
||||||
// Get the value of the member of root named 'encoding', return a 'null' value if
|
|
||||||
// there is no such member.
|
|
||||||
const Json::Value plugins = root["plug-ins"];
|
|
||||||
for ( int index = 0; index < plugins.size(); ++index ) // Iterates over the sequence elements.
|
|
||||||
loadPlugIn( plugins[index].asString() );
|
|
||||||
|
|
||||||
setIndentLength( root["indent"].get("length", 3).asInt() );
|
|
||||||
setIndentUseSpace( root["indent"].get("use_space", true).asBool() );
|
|
||||||
|
|
||||||
// ...
|
|
||||||
// At application shutdown to make the new configuration document:
|
|
||||||
// Since Json::Value has implicit constructor for all value types, it is not
|
|
||||||
// necessary to explicitly construct the Json::Value object:
|
|
||||||
root["encoding"] = getCurrentEncoding();
|
|
||||||
root["indent"]["length"] = getCurrentIndentLength();
|
|
||||||
root["indent"]["use_space"] = getCurrentIndentUseSpace();
|
|
||||||
|
|
||||||
Json::StyledWriter writer;
|
|
||||||
// Make a new JSON document for the configuration. Preserve original comments.
|
|
||||||
std::string outputConfig = writer.write( root );
|
|
||||||
|
|
||||||
// You can also use streams. This will put the contents of any JSON
|
|
||||||
// stream at a particular sub-value, if you'd like.
|
|
||||||
std::cin >> root["subtree"];
|
std::cin >> root["subtree"];
|
||||||
|
|
||||||
// And you can write to a stream, using the StyledWriter automatically.
|
// Get the value of the member of root named 'encoding',
|
||||||
|
// and return 'UTF-8' if there is no such member.
|
||||||
|
std::string encoding = root.get("encoding", "UTF-8" ).asString();
|
||||||
|
|
||||||
|
// Get the value of the member of root named 'plug-ins'; return a 'null' value if
|
||||||
|
// there is no such member.
|
||||||
|
const Json::Value plugins = root["plug-ins"];
|
||||||
|
|
||||||
|
// Iterate over the sequence elements.
|
||||||
|
for ( int index = 0; index < plugins.size(); ++index )
|
||||||
|
loadPlugIn( plugins[index].asString() );
|
||||||
|
|
||||||
|
// Try other datatypes. Some are auto-convertible to others.
|
||||||
|
foo::setIndentLength( root["indent"].get("length", 3).asInt() );
|
||||||
|
foo::setIndentUseSpace( root["indent"].get("use_space", true).asBool() );
|
||||||
|
|
||||||
|
// Since Json::Value has an implicit constructor for all value types, it is not
|
||||||
|
// necessary to explicitly construct the Json::Value object.
|
||||||
|
root["encoding"] = foo::getCurrentEncoding();
|
||||||
|
root["indent"]["length"] = foo::getCurrentIndentLength();
|
||||||
|
root["indent"]["use_space"] = foo::getCurrentIndentUseSpace();
|
||||||
|
|
||||||
|
// If you like the defaults, you can insert directly into a stream.
|
||||||
std::cout << root;
|
std::cout << root;
|
||||||
|
// Of course, you can write to `std::ostringstream` if you prefer.
|
||||||
|
|
||||||
|
// If desired, remember to add a linefeed and flush.
|
||||||
|
std::cout << std::endl;
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section _advanced Advanced usage
|
||||||
|
|
||||||
|
Configure *builders* to create *readers* and *writers*. For
|
||||||
|
configuration, we use our own `Json::Value` (rather than
|
||||||
|
standard setters/getters) so that we can add
|
||||||
|
features without losing binary-compatibility.
|
||||||
|
|
||||||
|
\code
|
||||||
|
// For convenience, use `writeString()` with a specialized builder.
|
||||||
|
Json::StreamWriterBuilder wbuilder;
|
||||||
|
wbuilder.settings_["indentation"] = "\t"; // simple Json::Value
|
||||||
|
std::string document = Json::writeString(wbuilder, root);
|
||||||
|
|
||||||
|
// Here, using a specialized Builder, we discard comments and
|
||||||
|
// record errors as we parse.
|
||||||
|
Json::CharReaderBuilder rbuilder;
|
||||||
|
rbuilder.settings_["collectComments"] = false; // simple Json::Value
|
||||||
|
std::string errs;
|
||||||
|
bool ok = Json::parseFromStream(rbuilder, std::cin, &root, &errs);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Yes, compile-time configuration-checking would be helpful,
|
||||||
|
but `Json::Value` lets you
|
||||||
|
write and read the builder configuration, which is better! In other words,
|
||||||
|
you can configure your JSON parser using JSON.
|
||||||
|
|
||||||
|
CharReaders and StreamWriters are not thread-safe, but they are re-usable.
|
||||||
|
\code
|
||||||
|
Json::CharReaderBuilder rbuilder;
|
||||||
|
cfg >> rbuilder.settings_;
|
||||||
|
std::unique_ptr<Json::CharReader> const reader(rbuilder.newCharReader());
|
||||||
|
reader->parse(start, stop, &value1, &errs);
|
||||||
|
// ...
|
||||||
|
reader->parse(start, stop, &value2, &errs);
|
||||||
|
// etc.
|
||||||
\endcode
|
\endcode
|
||||||
|
|
||||||
\section _pbuild Build instructions
|
\section _pbuild Build instructions
|
||||||
@@ -116,4 +156,9 @@ Basically JsonCpp is licensed under MIT license, or public domain if desired
|
|||||||
and recognized in your jurisdiction.
|
and recognized in your jurisdiction.
|
||||||
|
|
||||||
\author Baptiste Lepilleur <blep@users.sourceforge.net> (originator)
|
\author Baptiste Lepilleur <blep@users.sourceforge.net> (originator)
|
||||||
|
\author Christopher Dunn <cdunn2001@gmail.com> (primary maintainer)
|
||||||
|
\version \include version
|
||||||
|
We make strong guarantees about binary-compatibility, consistent with
|
||||||
|
<a href="http://apr.apache.org/versioning.html">the Apache versioning scheme</a>.
|
||||||
|
\sa version.h
|
||||||
*/
|
*/
|
||||||
|
2302
doc/web_doxyfile.in
Normal file
2302
doc/web_doxyfile.in
Normal file
File diff suppressed because it is too large
Load Diff
100
doxybuild.py
100
doxybuild.py
@@ -1,13 +1,28 @@
|
|||||||
"""Script to generate doxygen documentation.
|
"""Script to generate doxygen documentation.
|
||||||
"""
|
"""
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
from __future__ import unicode_literals
|
||||||
from devtools import tarball
|
from devtools import tarball
|
||||||
|
from contextlib import contextmanager
|
||||||
|
import subprocess
|
||||||
|
import traceback
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import os.path
|
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def cd(newdir):
|
||||||
|
"""
|
||||||
|
http://stackoverflow.com/questions/431684/how-do-i-cd-in-python
|
||||||
|
"""
|
||||||
|
prevdir = os.getcwd()
|
||||||
|
os.chdir(newdir)
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
os.chdir(prevdir)
|
||||||
|
|
||||||
def find_program(*filenames):
|
def find_program(*filenames):
|
||||||
"""find a program in folders path_lst, and sets env[var]
|
"""find a program in folders path_lst, and sets env[var]
|
||||||
@param filenames: a list of possible names of the program to search for
|
@param filenames: a list of possible names of the program to search for
|
||||||
@@ -28,51 +43,54 @@ def do_subst_in_file(targetfile, sourcefile, dict):
|
|||||||
For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
|
For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
|
||||||
then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
|
then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
|
||||||
"""
|
"""
|
||||||
try:
|
with open(sourcefile, 'r') as f:
|
||||||
f = open(sourcefile, 'rb')
|
|
||||||
contents = f.read()
|
contents = f.read()
|
||||||
f.close()
|
|
||||||
except:
|
|
||||||
print("Can't read source file %s"%sourcefile)
|
|
||||||
raise
|
|
||||||
for (k,v) in list(dict.items()):
|
for (k,v) in list(dict.items()):
|
||||||
v = v.replace('\\','\\\\')
|
v = v.replace('\\','\\\\')
|
||||||
contents = re.sub(k, v, contents)
|
contents = re.sub(k, v, contents)
|
||||||
try:
|
with open(targetfile, 'w') as f:
|
||||||
f = open(targetfile, 'wb')
|
|
||||||
f.write(contents)
|
f.write(contents)
|
||||||
f.close()
|
|
||||||
|
def getstatusoutput(cmd):
|
||||||
|
"""cmd is a list.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
output, _ = process.communicate()
|
||||||
|
status = process.returncode
|
||||||
except:
|
except:
|
||||||
print("Can't write target file %s"%targetfile)
|
status = -1
|
||||||
raise
|
output = traceback.format_exc()
|
||||||
|
return status, output
|
||||||
|
|
||||||
|
def run_cmd(cmd, silent=False):
|
||||||
|
"""Raise exception on failure.
|
||||||
|
"""
|
||||||
|
info = 'Running: %r in %r' %(' '.join(cmd), os.getcwd())
|
||||||
|
print(info)
|
||||||
|
sys.stdout.flush()
|
||||||
|
if silent:
|
||||||
|
status, output = getstatusoutput(cmd)
|
||||||
|
else:
|
||||||
|
status, output = os.system(' '.join(cmd)), ''
|
||||||
|
if status:
|
||||||
|
msg = 'Error while %s ...\n\terror=%d, output="""%s"""' %(info, status, output)
|
||||||
|
raise Exception(msg)
|
||||||
|
|
||||||
|
def assert_is_exe(path):
|
||||||
|
if not path:
|
||||||
|
raise Exception('path is empty.')
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
raise Exception('%r is not a file.' %path)
|
||||||
|
if not os.access(path, os.X_OK):
|
||||||
|
raise Exception('%r is not executable by this user.' %path)
|
||||||
|
|
||||||
def run_doxygen(doxygen_path, config_file, working_dir, is_silent):
|
def run_doxygen(doxygen_path, config_file, working_dir, is_silent):
|
||||||
|
assert_is_exe(doxygen_path)
|
||||||
config_file = os.path.abspath(config_file)
|
config_file = os.path.abspath(config_file)
|
||||||
doxygen_path = doxygen_path
|
with cd(working_dir):
|
||||||
old_cwd = os.getcwd()
|
|
||||||
try:
|
|
||||||
os.chdir( working_dir )
|
|
||||||
cmd = [doxygen_path, config_file]
|
cmd = [doxygen_path, config_file]
|
||||||
print('Running:', ' '.join( cmd ))
|
run_cmd(cmd, is_silent)
|
||||||
try:
|
|
||||||
import subprocess
|
|
||||||
except:
|
|
||||||
if os.system( ' '.join( cmd ) ) != 0:
|
|
||||||
print('Documentation generation failed')
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
if is_silent:
|
|
||||||
process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
|
|
||||||
else:
|
|
||||||
process = subprocess.Popen( cmd )
|
|
||||||
stdout, _ = process.communicate()
|
|
||||||
if process.returncode:
|
|
||||||
print('Documentation generation failed:')
|
|
||||||
print(stdout)
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
finally:
|
|
||||||
os.chdir( old_cwd )
|
|
||||||
|
|
||||||
def build_doc(options, make_release=False):
|
def build_doc(options, make_release=False):
|
||||||
if make_release:
|
if make_release:
|
||||||
@@ -112,10 +130,10 @@ def build_doc( options, make_release=False ):
|
|||||||
if not os.path.isdir(output_dir):
|
if not os.path.isdir(output_dir):
|
||||||
os.makedirs(output_dir)
|
os.makedirs(output_dir)
|
||||||
|
|
||||||
do_subst_in_file( 'doc/doxyfile', 'doc/doxyfile.in', subst_keys )
|
do_subst_in_file('doc/doxyfile', options.doxyfile_input_path, subst_keys)
|
||||||
ok = run_doxygen( options.doxygen_path, 'doc/doxyfile', 'doc', is_silent=options.silent )
|
run_doxygen(options.doxygen_path, 'doc/doxyfile', 'doc', is_silent=options.silent)
|
||||||
if not options.silent:
|
if not options.silent:
|
||||||
print(open(warning_log_path, 'rb').read())
|
print(open(warning_log_path, 'r').read())
|
||||||
index_path = os.path.abspath(os.path.join('doc', subst_keys['%HTML_OUTPUT%'], 'index.html'))
|
index_path = os.path.abspath(os.path.join('doc', subst_keys['%HTML_OUTPUT%'], 'index.html'))
|
||||||
print('Generated documentation can be found in:')
|
print('Generated documentation can be found in:')
|
||||||
print(index_path)
|
print(index_path)
|
||||||
@@ -126,7 +144,7 @@ def build_doc( options, make_release=False ):
|
|||||||
print('Generating doc tarball to', tarball_path)
|
print('Generating doc tarball to', tarball_path)
|
||||||
tarball_sources = [
|
tarball_sources = [
|
||||||
output_dir,
|
output_dir,
|
||||||
'README.txt',
|
'README.md',
|
||||||
'LICENSE',
|
'LICENSE',
|
||||||
'NEWS.txt',
|
'NEWS.txt',
|
||||||
'version'
|
'version'
|
||||||
@@ -151,6 +169,8 @@ def main():
|
|||||||
help="""Path to GraphViz dot tool. Must be full qualified path. [Default: %default]""")
|
help="""Path to GraphViz dot tool. Must be full qualified path. [Default: %default]""")
|
||||||
parser.add_option('--doxygen', dest="doxygen_path", action='store', default=find_program('doxygen'),
|
parser.add_option('--doxygen', dest="doxygen_path", action='store', default=find_program('doxygen'),
|
||||||
help="""Path to Doxygen tool. [Default: %default]""")
|
help="""Path to Doxygen tool. [Default: %default]""")
|
||||||
|
parser.add_option('--in', dest="doxyfile_input_path", action='store', default='doc/doxyfile.in',
|
||||||
|
help="""Path to doxygen inputs. [Default: %default]""")
|
||||||
parser.add_option('--with-html-help', dest="with_html_help", action='store_true', default=False,
|
parser.add_option('--with-html-help', dest="with_html_help", action='store_true', default=False,
|
||||||
help="""Enable generation of Microsoft HTML HELP""")
|
help="""Enable generation of Microsoft HTML HELP""")
|
||||||
parser.add_option('--no-uml-look', dest="with_uml_look", action='store_false', default=True,
|
parser.add_option('--no-uml-look', dest="with_uml_look", action='store_false', default=True,
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <istream>
|
||||||
|
|
||||||
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
|
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
|
||||||
// be used by...
|
// be used by...
|
||||||
@@ -27,6 +28,7 @@ namespace Json {
|
|||||||
/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
|
/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
|
||||||
*Value.
|
*Value.
|
||||||
*
|
*
|
||||||
|
* \deprecated Use CharReader and CharReaderBuilder.
|
||||||
*/
|
*/
|
||||||
class JSON_API Reader {
|
class JSON_API Reader {
|
||||||
public:
|
public:
|
||||||
@@ -78,7 +80,7 @@ public:
|
|||||||
document to read.
|
document to read.
|
||||||
* \param endDoc Pointer on the end of the UTF-8 encoded string of the
|
* \param endDoc Pointer on the end of the UTF-8 encoded string of the
|
||||||
document to read.
|
document to read.
|
||||||
\ Must be >= beginDoc.
|
* Must be >= beginDoc.
|
||||||
* \param root [out] Contains the root value of the document if it was
|
* \param root [out] Contains the root value of the document if it was
|
||||||
* successfully parsed.
|
* successfully parsed.
|
||||||
* \param collectComments \c true to collect comment and allow writing them
|
* \param collectComments \c true to collect comment and allow writing them
|
||||||
@@ -238,8 +240,124 @@ private:
|
|||||||
std::string commentsBefore_;
|
std::string commentsBefore_;
|
||||||
Features features_;
|
Features features_;
|
||||||
bool collectComments_;
|
bool collectComments_;
|
||||||
|
}; // Reader
|
||||||
|
|
||||||
|
/** Interface for reading JSON from a char array.
|
||||||
|
*/
|
||||||
|
class JSON_API CharReader {
|
||||||
|
public:
|
||||||
|
virtual ~CharReader() {}
|
||||||
|
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
|
||||||
|
document.
|
||||||
|
* The document must be a UTF-8 encoded string containing the document to read.
|
||||||
|
*
|
||||||
|
* \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
|
||||||
|
document to read.
|
||||||
|
* \param endDoc Pointer on the end of the UTF-8 encoded string of the
|
||||||
|
document to read.
|
||||||
|
* Must be >= beginDoc.
|
||||||
|
* \param root [out] Contains the root value of the document if it was
|
||||||
|
* successfully parsed.
|
||||||
|
* \param errs [out] Formatted error messages (if not NULL)
|
||||||
|
* a user friendly string that lists errors in the parsed
|
||||||
|
* document.
|
||||||
|
* \return \c true if the document was successfully parsed, \c false if an
|
||||||
|
error occurred.
|
||||||
|
*/
|
||||||
|
virtual bool parse(
|
||||||
|
char const* beginDoc, char const* endDoc,
|
||||||
|
Value* root, std::string* errs) = 0;
|
||||||
|
|
||||||
|
class Factory {
|
||||||
|
public:
|
||||||
|
/** \brief Allocate a CharReader via operator new().
|
||||||
|
* \throw std::exception if something goes wrong (e.g. invalid settings)
|
||||||
|
*/
|
||||||
|
virtual CharReader* newCharReader() const = 0;
|
||||||
|
}; // Factory
|
||||||
|
}; // CharReader
|
||||||
|
|
||||||
|
/** \brief Build a CharReader implementation.
|
||||||
|
|
||||||
|
\deprecated This is experimental and will be altered before the next release.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
\code
|
||||||
|
using namespace Json;
|
||||||
|
CharReaderBuilder builder;
|
||||||
|
builder.settings_["collectComments"] = false;
|
||||||
|
Value value;
|
||||||
|
std::string errs;
|
||||||
|
bool ok = parseFromStream(builder, std::cin, &value, &errs);
|
||||||
|
\endcode
|
||||||
|
*/
|
||||||
|
class JSON_API CharReaderBuilder : public CharReader::Factory {
|
||||||
|
public:
|
||||||
|
// Note: We use a Json::Value so that we can add data-members to this class
|
||||||
|
// without a major version bump.
|
||||||
|
/** Configuration of this builder.
|
||||||
|
These are case-sensitive.
|
||||||
|
Available settings (case-sensitive):
|
||||||
|
- `"collectComments": false or true`
|
||||||
|
- true to collect comment and allow writing them
|
||||||
|
back during serialization, false to discard comments.
|
||||||
|
This parameter is ignored if allowComments is false.
|
||||||
|
- `"allowComments": false or true`
|
||||||
|
- true if comments are allowed.
|
||||||
|
- `"strictRoot": false or true`
|
||||||
|
- true if root must be either an array or an object value
|
||||||
|
- `"allowDroppedNullPlaceholders": false or true`
|
||||||
|
- true if dropped null placeholders are allowed. (See StreamWriterBuilder.)
|
||||||
|
- `"allowNumericKeys": false or true`
|
||||||
|
- true if numeric object keys are allowed.
|
||||||
|
- `"stackLimit": integer`
|
||||||
|
- Exceeding stackLimit (recursive depth of `readValue()`) will
|
||||||
|
cause an exception.
|
||||||
|
- This is a security issue (seg-faults caused by deeply nested JSON),
|
||||||
|
so the default is low.
|
||||||
|
- `"failIfExtra": false or true`
|
||||||
|
- If true, `parse()` returns false when extra non-whitespace trails
|
||||||
|
the JSON value in the input string.
|
||||||
|
|
||||||
|
You can examine 'settings_` yourself
|
||||||
|
to see the defaults. You can also write and read them just like any
|
||||||
|
JSON Value.
|
||||||
|
\sa setDefaults()
|
||||||
|
*/
|
||||||
|
Json::Value settings_;
|
||||||
|
|
||||||
|
CharReaderBuilder();
|
||||||
|
virtual ~CharReaderBuilder();
|
||||||
|
|
||||||
|
virtual CharReader* newCharReader() const;
|
||||||
|
|
||||||
|
/** \return true if 'settings' are legal and consistent;
|
||||||
|
* otherwise, indicate bad settings via 'invalid'.
|
||||||
|
*/
|
||||||
|
bool validate(Json::Value* invalid) const;
|
||||||
|
/** Called by ctor, but you can use this to reset settings_.
|
||||||
|
* \pre 'settings' != NULL (but Json::null is fine)
|
||||||
|
* \remark Defaults:
|
||||||
|
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode
|
||||||
|
*/
|
||||||
|
static void setDefaults(Json::Value* settings);
|
||||||
|
/** Same as old Features::strictMode().
|
||||||
|
* \pre 'settings' != NULL (but Json::null is fine)
|
||||||
|
* \remark Defaults:
|
||||||
|
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults
|
||||||
|
*/
|
||||||
|
static void strictMode(Json::Value* settings);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Consume entire stream and use its begin/end.
|
||||||
|
* Someday we might have a real StreamReader, but for now this
|
||||||
|
* is convenient.
|
||||||
|
*/
|
||||||
|
bool parseFromStream(
|
||||||
|
CharReader::Factory const&,
|
||||||
|
std::istream&,
|
||||||
|
Value* root, std::string* errs);
|
||||||
|
|
||||||
/** \brief Read from 'sin' into 'root'.
|
/** \brief Read from 'sin' into 'root'.
|
||||||
|
|
||||||
Always keep comments from the input JSON.
|
Always keep comments from the input JSON.
|
||||||
|
@@ -133,7 +133,8 @@ public:
|
|||||||
typedef Json::LargestUInt LargestUInt;
|
typedef Json::LargestUInt LargestUInt;
|
||||||
typedef Json::ArrayIndex ArrayIndex;
|
typedef Json::ArrayIndex ArrayIndex;
|
||||||
|
|
||||||
static const Value& null;
|
static const Value& null; ///! We regret this reference to a global instance; prefer the simpler Value().
|
||||||
|
static const Value& nullRef; ///! just a kludge for binary-compatibility; same as null
|
||||||
/// Minimum signed integer value that can be stored in a Json::Value.
|
/// Minimum signed integer value that can be stored in a Json::Value.
|
||||||
static const LargestInt minLargestInt;
|
static const LargestInt minLargestInt;
|
||||||
/// Maximum signed integer value that can be stored in a Json::Value.
|
/// Maximum signed integer value that can be stored in a Json::Value.
|
||||||
@@ -432,9 +433,11 @@ Json::Value obj_value(Json::objectValue); // {}
|
|||||||
// EnumValues enumValues() const;
|
// EnumValues enumValues() const;
|
||||||
//# endif
|
//# endif
|
||||||
|
|
||||||
/// Comments must be //... or /* ... */
|
/// \deprecated Always pass len.
|
||||||
void setComment(const char* comment, CommentPlacement placement);
|
void setComment(const char* comment, CommentPlacement placement);
|
||||||
/// Comments must be //... or /* ... */
|
/// Comments must be //... or /* ... */
|
||||||
|
void setComment(const char* comment, size_t len, CommentPlacement placement);
|
||||||
|
/// Comments must be //... or /* ... */
|
||||||
void setComment(const std::string& comment, CommentPlacement placement);
|
void setComment(const std::string& comment, CommentPlacement placement);
|
||||||
bool hasComment(CommentPlacement placement) const;
|
bool hasComment(CommentPlacement placement) const;
|
||||||
/// Include delimiters and embedded newlines.
|
/// Include delimiters and embedded newlines.
|
||||||
@@ -477,7 +480,7 @@ private:
|
|||||||
CommentInfo();
|
CommentInfo();
|
||||||
~CommentInfo();
|
~CommentInfo();
|
||||||
|
|
||||||
void setComment(const char* text);
|
void setComment(const char* text, size_t len);
|
||||||
|
|
||||||
char* comment_;
|
char* comment_;
|
||||||
};
|
};
|
||||||
@@ -505,10 +508,10 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
} value_;
|
} value_;
|
||||||
ValueType type_ : 8;
|
ValueType type_ : 8;
|
||||||
int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.
|
unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.
|
||||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||||
unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container.
|
unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container.
|
||||||
int memberNameIsStatic_ : 1; // used by the ValueInternalMap container.
|
unsigned int memberNameIsStatic_ : 1; // used by the ValueInternalMap container.
|
||||||
#endif
|
#endif
|
||||||
CommentInfo* comments_;
|
CommentInfo* comments_;
|
||||||
|
|
||||||
|
@@ -4,10 +4,10 @@
|
|||||||
#ifndef JSON_VERSION_H_INCLUDED
|
#ifndef JSON_VERSION_H_INCLUDED
|
||||||
# define JSON_VERSION_H_INCLUDED
|
# define JSON_VERSION_H_INCLUDED
|
||||||
|
|
||||||
# define JSONCPP_VERSION_STRING "1.3.0"
|
# define JSONCPP_VERSION_STRING "1.4.1"
|
||||||
# define JSONCPP_VERSION_MAJOR 1
|
# define JSONCPP_VERSION_MAJOR 1
|
||||||
# define JSONCPP_VERSION_MINOR 3
|
# define JSONCPP_VERSION_MINOR 4
|
||||||
# define JSONCPP_VERSION_PATCH 0
|
# define JSONCPP_VERSION_PATCH 1
|
||||||
# define JSONCPP_VERSION_QUALIFIER
|
# define JSONCPP_VERSION_QUALIFIER
|
||||||
# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))
|
# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
|
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
|
||||||
// be used by...
|
// be used by...
|
||||||
@@ -23,7 +24,111 @@ namespace Json {
|
|||||||
|
|
||||||
class Value;
|
class Value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
\code
|
||||||
|
using namespace Json;
|
||||||
|
void writeToStdout(StreamWriter::Factory const& factory, Value const& value) {
|
||||||
|
std::unique_ptr<StreamWriter> const writer(
|
||||||
|
factory.newStreamWriter());
|
||||||
|
writer->write(value, &std::cout);
|
||||||
|
std::cout << std::endl; // add lf and flush
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
*/
|
||||||
|
class JSON_API StreamWriter {
|
||||||
|
protected:
|
||||||
|
std::ostream* sout_; // not owned; will not delete
|
||||||
|
public:
|
||||||
|
StreamWriter();
|
||||||
|
virtual ~StreamWriter();
|
||||||
|
/** Write Value into document as configured in sub-class.
|
||||||
|
Do not take ownership of sout, but maintain a reference during function.
|
||||||
|
\pre sout != NULL
|
||||||
|
\return zero on success
|
||||||
|
\throw std::exception possibly, depending on configuration
|
||||||
|
*/
|
||||||
|
virtual int write(Value const& root, std::ostream* sout) = 0;
|
||||||
|
|
||||||
|
/** \brief A simple abstract factory.
|
||||||
|
*/
|
||||||
|
class JSON_API Factory {
|
||||||
|
public:
|
||||||
|
virtual ~Factory();
|
||||||
|
/** \brief Allocate a CharReader via operator new().
|
||||||
|
* \throw std::exception if something goes wrong (e.g. invalid settings)
|
||||||
|
*/
|
||||||
|
virtual StreamWriter* newStreamWriter() const = 0;
|
||||||
|
}; // Factory
|
||||||
|
}; // StreamWriter
|
||||||
|
|
||||||
|
/** \brief Write into stringstream, then return string, for convenience.
|
||||||
|
* A StreamWriter will be created from the factory, used, and then deleted.
|
||||||
|
*/
|
||||||
|
std::string writeString(StreamWriter::Factory const& factory, Value const& root);
|
||||||
|
|
||||||
|
|
||||||
|
/** \brief Build a StreamWriter implementation.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
\code
|
||||||
|
using namespace Json;
|
||||||
|
Value value = ...;
|
||||||
|
StreamWriterBuilder builder;
|
||||||
|
builder.settings_["commentStyle"] = "None";
|
||||||
|
builder.settings_["indentation"] = " "; // or whatever you like
|
||||||
|
std::unique_ptr<Json::StreamWriter> writer(
|
||||||
|
builder.newStreamWriter());
|
||||||
|
writer->write(value, &std::cout);
|
||||||
|
std::cout << std::endl; // add lf and flush
|
||||||
|
\endcode
|
||||||
|
*/
|
||||||
|
class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
|
||||||
|
public:
|
||||||
|
// Note: We use a Json::Value so that we can add data-members to this class
|
||||||
|
// without a major version bump.
|
||||||
|
/** Configuration of this builder.
|
||||||
|
Available settings (case-sensitive):
|
||||||
|
- "commentStyle": "None" or "All"
|
||||||
|
- "indentation": "<anything>"
|
||||||
|
- "enableYAMLCompatibility": false or true
|
||||||
|
- slightly change the whitespace around colons
|
||||||
|
- "dropNullPlaceholders": false or true
|
||||||
|
- Drop the "null" string from the writer's output for nullValues.
|
||||||
|
Strictly speaking, this is not valid JSON. But when the output is being
|
||||||
|
fed to a browser's Javascript, it makes for smaller output and the
|
||||||
|
browser can handle the output just fine.
|
||||||
|
|
||||||
|
You can examine 'settings_` yourself
|
||||||
|
to see the defaults. You can also write and read them just like any
|
||||||
|
JSON Value.
|
||||||
|
\sa setDefaults()
|
||||||
|
*/
|
||||||
|
Json::Value settings_;
|
||||||
|
|
||||||
|
StreamWriterBuilder();
|
||||||
|
virtual ~StreamWriterBuilder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \throw std::exception if something goes wrong (e.g. invalid settings)
|
||||||
|
*/
|
||||||
|
virtual StreamWriter* newStreamWriter() const;
|
||||||
|
|
||||||
|
/** \return true if 'settings' are legal and consistent;
|
||||||
|
* otherwise, indicate bad settings via 'invalid'.
|
||||||
|
*/
|
||||||
|
bool validate(Json::Value* invalid) const;
|
||||||
|
/** Called by ctor, but you can use this to reset settings_.
|
||||||
|
* \pre 'settings' != NULL (but Json::null is fine)
|
||||||
|
* \remark Defaults:
|
||||||
|
* \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults
|
||||||
|
*/
|
||||||
|
static void setDefaults(Json::Value* settings);
|
||||||
|
};
|
||||||
|
|
||||||
/** \brief Abstract class for writers.
|
/** \brief Abstract class for writers.
|
||||||
|
* \deprecated Use StreamWriter.
|
||||||
*/
|
*/
|
||||||
class JSON_API Writer {
|
class JSON_API Writer {
|
||||||
public:
|
public:
|
||||||
@@ -39,6 +144,7 @@ public:
|
|||||||
*consumption,
|
*consumption,
|
||||||
* but may be usefull to support feature such as RPC where bandwith is limited.
|
* but may be usefull to support feature such as RPC where bandwith is limited.
|
||||||
* \sa Reader, Value
|
* \sa Reader, Value
|
||||||
|
* \deprecated Use StreamWriterBuilder.
|
||||||
*/
|
*/
|
||||||
class JSON_API FastWriter : public Writer {
|
class JSON_API FastWriter : public Writer {
|
||||||
public:
|
public:
|
||||||
@@ -90,6 +196,7 @@ private:
|
|||||||
*#CommentPlacement.
|
*#CommentPlacement.
|
||||||
*
|
*
|
||||||
* \sa Reader, Value, Value::setComment()
|
* \sa Reader, Value, Value::setComment()
|
||||||
|
* \deprecated Use StreamWriterBuilder.
|
||||||
*/
|
*/
|
||||||
class JSON_API StyledWriter : public Writer {
|
class JSON_API StyledWriter : public Writer {
|
||||||
public:
|
public:
|
||||||
@@ -151,6 +258,7 @@ private:
|
|||||||
*
|
*
|
||||||
* \param indentation Each level will be indented by this amount extra.
|
* \param indentation Each level will be indented by this amount extra.
|
||||||
* \sa Reader, Value, Value::setComment()
|
* \sa Reader, Value, Value::setComment()
|
||||||
|
* \deprecated Use StreamWriterBuilder.
|
||||||
*/
|
*/
|
||||||
class JSON_API StyledStreamWriter {
|
class JSON_API StyledStreamWriter {
|
||||||
public:
|
public:
|
||||||
@@ -187,7 +295,8 @@ private:
|
|||||||
std::string indentString_;
|
std::string indentString_;
|
||||||
int rightMargin_;
|
int rightMargin_;
|
||||||
std::string indentation_;
|
std::string indentation_;
|
||||||
bool addChildValues_;
|
bool addChildValues_ : 1;
|
||||||
|
bool indented_ : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(JSON_HAS_INT64)
|
#if defined(JSON_HAS_INT64)
|
||||||
|
@@ -161,8 +161,7 @@ def generate(env):
|
|||||||
Add builders and construction variables for the
|
Add builders and construction variables for the
|
||||||
SrcDist tool.
|
SrcDist tool.
|
||||||
"""
|
"""
|
||||||
## doxyfile_scanner = env.Scanner(
|
## doxyfile_scanner = env.Scanner(## DoxySourceScan,
|
||||||
## DoxySourceScan,
|
|
||||||
## "DoxySourceScan",
|
## "DoxySourceScan",
|
||||||
## scan_check = DoxySourceScanCheck,
|
## scan_check = DoxySourceScanCheck,
|
||||||
##)
|
##)
|
||||||
|
@@ -7,7 +7,13 @@ ENDIF(JSONCPP_LIB_BUILD_SHARED)
|
|||||||
ADD_EXECUTABLE(jsontestrunner_exe
|
ADD_EXECUTABLE(jsontestrunner_exe
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
IF(JSONCPP_LIB_BUILD_SHARED)
|
||||||
TARGET_LINK_LIBRARIES(jsontestrunner_exe jsoncpp_lib)
|
TARGET_LINK_LIBRARIES(jsontestrunner_exe jsoncpp_lib)
|
||||||
|
ELSE(JSONCPP_LIB_BUILD_SHARED)
|
||||||
|
TARGET_LINK_LIBRARIES(jsontestrunner_exe jsoncpp_lib_static)
|
||||||
|
ENDIF(JSONCPP_LIB_BUILD_SHARED)
|
||||||
|
|
||||||
SET_TARGET_PROPERTIES(jsontestrunner_exe PROPERTIES OUTPUT_NAME jsontestrunner_exe)
|
SET_TARGET_PROPERTIES(jsontestrunner_exe PROPERTIES OUTPUT_NAME jsontestrunner_exe)
|
||||||
|
|
||||||
IF(PYTHONINTERP_FOUND)
|
IF(PYTHONINTERP_FOUND)
|
||||||
|
@@ -8,12 +8,22 @@
|
|||||||
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
#include <algorithm> // sort
|
#include <algorithm> // sort
|
||||||
|
#include <sstream>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#if defined(_MSC_VER) && _MSC_VER >= 1310
|
#if defined(_MSC_VER) && _MSC_VER >= 1310
|
||||||
#pragma warning(disable : 4996) // disable fopen deprecation warning
|
#pragma warning(disable : 4996) // disable fopen deprecation warning
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct Options
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
Json::Features features;
|
||||||
|
bool parseOnly;
|
||||||
|
typedef std::string (*writeFuncType)(Json::Value const&);
|
||||||
|
writeFuncType write;
|
||||||
|
};
|
||||||
|
|
||||||
static std::string normalizeFloatingPointStr(double value) {
|
static std::string normalizeFloatingPointStr(double value) {
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
|
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
|
||||||
@@ -129,43 +139,67 @@ printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") {
|
|||||||
static int parseAndSaveValueTree(const std::string& input,
|
static int parseAndSaveValueTree(const std::string& input,
|
||||||
const std::string& actual,
|
const std::string& actual,
|
||||||
const std::string& kind,
|
const std::string& kind,
|
||||||
Json::Value& root,
|
|
||||||
const Json::Features& features,
|
const Json::Features& features,
|
||||||
bool parseOnly) {
|
bool parseOnly,
|
||||||
|
Json::Value* root)
|
||||||
|
{
|
||||||
Json::Reader reader(features);
|
Json::Reader reader(features);
|
||||||
bool parsingSuccessful = reader.parse(input, root);
|
bool parsingSuccessful = reader.parse(input, *root);
|
||||||
if (!parsingSuccessful) {
|
if (!parsingSuccessful) {
|
||||||
printf("Failed to parse %s file: \n%s\n",
|
printf("Failed to parse %s file: \n%s\n",
|
||||||
kind.c_str(),
|
kind.c_str(),
|
||||||
reader.getFormattedErrorMessages().c_str());
|
reader.getFormattedErrorMessages().c_str());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parseOnly) {
|
if (!parseOnly) {
|
||||||
FILE* factual = fopen(actual.c_str(), "wt");
|
FILE* factual = fopen(actual.c_str(), "wt");
|
||||||
if (!factual) {
|
if (!factual) {
|
||||||
printf("Failed to create %s actual file.\n", kind.c_str());
|
printf("Failed to create %s actual file.\n", kind.c_str());
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
printValueTree(factual, root);
|
printValueTree(factual, *root);
|
||||||
fclose(factual);
|
fclose(factual);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
// static std::string useFastWriter(Json::Value const& root) {
|
||||||
static int rewriteValueTree(const std::string& rewritePath,
|
|
||||||
const Json::Value& root,
|
|
||||||
std::string& rewrite) {
|
|
||||||
// Json::FastWriter writer;
|
// Json::FastWriter writer;
|
||||||
// writer.enableYAMLCompatibility();
|
// writer.enableYAMLCompatibility();
|
||||||
|
// return writer.write(root);
|
||||||
|
// }
|
||||||
|
static std::string useStyledWriter(
|
||||||
|
Json::Value const& root)
|
||||||
|
{
|
||||||
Json::StyledWriter writer;
|
Json::StyledWriter writer;
|
||||||
rewrite = writer.write(root);
|
return writer.write(root);
|
||||||
|
}
|
||||||
|
static std::string useStyledStreamWriter(
|
||||||
|
Json::Value const& root)
|
||||||
|
{
|
||||||
|
Json::StyledStreamWriter writer;
|
||||||
|
std::ostringstream sout;
|
||||||
|
writer.write(sout, root);
|
||||||
|
return sout.str();
|
||||||
|
}
|
||||||
|
static std::string useBuiltStyledStreamWriter(
|
||||||
|
Json::Value const& root)
|
||||||
|
{
|
||||||
|
Json::StreamWriterBuilder builder;
|
||||||
|
return Json::writeString(builder, root);
|
||||||
|
}
|
||||||
|
static int rewriteValueTree(
|
||||||
|
const std::string& rewritePath,
|
||||||
|
const Json::Value& root,
|
||||||
|
Options::writeFuncType write,
|
||||||
|
std::string* rewrite)
|
||||||
|
{
|
||||||
|
*rewrite = write(root);
|
||||||
FILE* fout = fopen(rewritePath.c_str(), "wt");
|
FILE* fout = fopen(rewritePath.c_str(), "wt");
|
||||||
if (!fout) {
|
if (!fout) {
|
||||||
printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
|
printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
fprintf(fout, "%s\n", rewrite.c_str());
|
fprintf(fout, "%s\n", rewrite->c_str());
|
||||||
fclose(fout);
|
fclose(fout);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -194,84 +228,98 @@ static int printUsage(const char* argv[]) {
|
|||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parseCommandLine(int argc,
|
static int parseCommandLine(
|
||||||
const char* argv[],
|
int argc, const char* argv[], Options* opts)
|
||||||
Json::Features& features,
|
{
|
||||||
std::string& path,
|
opts->parseOnly = false;
|
||||||
bool& parseOnly) {
|
opts->write = &useStyledWriter;
|
||||||
parseOnly = false;
|
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
return printUsage(argv);
|
return printUsage(argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = 1;
|
int index = 1;
|
||||||
if (std::string(argv[1]) == "--json-checker") {
|
if (std::string(argv[index]) == "--json-checker") {
|
||||||
features = Json::Features::strictMode();
|
opts->features = Json::Features::strictMode();
|
||||||
parseOnly = true;
|
opts->parseOnly = true;
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
|
if (std::string(argv[index]) == "--json-config") {
|
||||||
if (std::string(argv[1]) == "--json-config") {
|
|
||||||
printConfig();
|
printConfig();
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
if (std::string(argv[index]) == "--json-writer") {
|
||||||
|
++index;
|
||||||
|
std::string const writerName(argv[index++]);
|
||||||
|
if (writerName == "StyledWriter") {
|
||||||
|
opts->write = &useStyledWriter;
|
||||||
|
} else if (writerName == "StyledStreamWriter") {
|
||||||
|
opts->write = &useStyledStreamWriter;
|
||||||
|
} else if (writerName == "BuiltStyledStreamWriter") {
|
||||||
|
opts->write = &useBuiltStyledStreamWriter;
|
||||||
|
} else {
|
||||||
|
printf("Unknown '--json-writer %s'\n", writerName.c_str());
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (index == argc || index + 1 < argc) {
|
if (index == argc || index + 1 < argc) {
|
||||||
return printUsage(argv);
|
return printUsage(argv);
|
||||||
}
|
}
|
||||||
|
opts->path = argv[index];
|
||||||
path = argv[index];
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
static int runTest(Options const& opts)
|
||||||
|
{
|
||||||
|
int exitCode = 0;
|
||||||
|
|
||||||
int main(int argc, const char* argv[]) {
|
std::string input = readInputTestFile(opts.path.c_str());
|
||||||
std::string path;
|
|
||||||
Json::Features features;
|
|
||||||
bool parseOnly;
|
|
||||||
int exitCode = parseCommandLine(argc, argv, features, path, parseOnly);
|
|
||||||
if (exitCode != 0) {
|
|
||||||
return exitCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string input = readInputTestFile(path.c_str());
|
|
||||||
if (input.empty()) {
|
if (input.empty()) {
|
||||||
printf("Failed to read input or empty input: %s\n", path.c_str());
|
printf("Failed to read input or empty input: %s\n", opts.path.c_str());
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string basePath = removeSuffix(argv[1], ".json");
|
std::string basePath = removeSuffix(opts.path, ".json");
|
||||||
if (!parseOnly && basePath.empty()) {
|
if (!opts.parseOnly && basePath.empty()) {
|
||||||
printf("Bad input path. Path does not end with '.expected':\n%s\n",
|
printf("Bad input path. Path does not end with '.expected':\n%s\n",
|
||||||
path.c_str());
|
opts.path.c_str());
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string actualPath = basePath + ".actual";
|
std::string const actualPath = basePath + ".actual";
|
||||||
std::string rewritePath = basePath + ".rewrite";
|
std::string const rewritePath = basePath + ".rewrite";
|
||||||
std::string rewriteActualPath = basePath + ".actual-rewrite";
|
std::string const rewriteActualPath = basePath + ".actual-rewrite";
|
||||||
|
|
||||||
Json::Value root;
|
Json::Value root;
|
||||||
exitCode = parseAndSaveValueTree(
|
exitCode = parseAndSaveValueTree(
|
||||||
input, actualPath, "input", root, features, parseOnly);
|
input, actualPath, "input",
|
||||||
if (exitCode == 0 && !parseOnly) {
|
opts.features, opts.parseOnly, &root);
|
||||||
|
if (exitCode || opts.parseOnly) {
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
std::string rewrite;
|
std::string rewrite;
|
||||||
exitCode = rewriteValueTree(rewritePath, root, rewrite);
|
exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
|
||||||
if (exitCode == 0) {
|
if (exitCode) {
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
Json::Value rewriteRoot;
|
Json::Value rewriteRoot;
|
||||||
exitCode = parseAndSaveValueTree(rewrite,
|
exitCode = parseAndSaveValueTree(
|
||||||
rewriteActualPath,
|
rewrite, rewriteActualPath, "rewrite",
|
||||||
"rewrite",
|
opts.features, opts.parseOnly, &rewriteRoot);
|
||||||
rewriteRoot,
|
if (exitCode) {
|
||||||
features,
|
return exitCode;
|
||||||
parseOnly);
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
int main(int argc, const char* argv[]) {
|
||||||
|
Options opts;
|
||||||
|
int exitCode = parseCommandLine(argc, argv, &opts);
|
||||||
|
if (exitCode != 0) {
|
||||||
|
printf("Failed to parse command-line.");
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return runTest(opts);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
printf("Unhandled exception:\n%s\n", e.what());
|
printf("Unhandled exception:\n%s\n", e.what());
|
||||||
exitCode = 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return exitCode;
|
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,10 @@
|
|||||||
OPTION(JSONCPP_LIB_BUILD_SHARED "Build jsoncpp_lib as a shared library." OFF)
|
OPTION(JSONCPP_LIB_BUILD_SHARED "Build jsoncpp_lib as a shared library." OFF)
|
||||||
|
OPTION(JSONCPP_LIB_BUILD_STATIC "Build jsoncpp_lib static library." ON)
|
||||||
|
|
||||||
IF(BUILD_SHARED_LIBS)
|
IF(BUILD_SHARED_LIBS)
|
||||||
SET(JSONCPP_LIB_BUILD_SHARED ON)
|
SET(JSONCPP_LIB_BUILD_SHARED ON)
|
||||||
ENDIF(BUILD_SHARED_LIBS)
|
ENDIF(BUILD_SHARED_LIBS)
|
||||||
|
|
||||||
IF(JSONCPP_LIB_BUILD_SHARED)
|
|
||||||
SET(JSONCPP_LIB_TYPE SHARED)
|
|
||||||
ADD_DEFINITIONS( -DJSON_DLL_BUILD )
|
|
||||||
ELSE(JSONCPP_LIB_BUILD_SHARED)
|
|
||||||
SET(JSONCPP_LIB_TYPE STATIC)
|
|
||||||
ENDIF(JSONCPP_LIB_BUILD_SHARED)
|
|
||||||
|
|
||||||
if( CMAKE_COMPILER_IS_GNUCXX )
|
if( CMAKE_COMPILER_IS_GNUCXX )
|
||||||
#Get compiler version.
|
#Get compiler version.
|
||||||
execute_process( COMMAND ${CMAKE_CXX_COMPILER} -dumpversion
|
execute_process( COMMAND ${CMAKE_CXX_COMPILER} -dumpversion
|
||||||
@@ -36,25 +31,14 @@ SET( PUBLIC_HEADERS
|
|||||||
|
|
||||||
SOURCE_GROUP( "Public API" FILES ${PUBLIC_HEADERS} )
|
SOURCE_GROUP( "Public API" FILES ${PUBLIC_HEADERS} )
|
||||||
|
|
||||||
ADD_LIBRARY( jsoncpp_lib ${JSONCPP_LIB_TYPE}
|
SET(jsoncpp_sources
|
||||||
${PUBLIC_HEADERS}
|
|
||||||
json_tool.h
|
json_tool.h
|
||||||
json_reader.cpp
|
json_reader.cpp
|
||||||
json_batchallocator.h
|
json_batchallocator.h
|
||||||
json_valueiterator.inl
|
json_valueiterator.inl
|
||||||
json_value.cpp
|
json_value.cpp
|
||||||
json_writer.cpp
|
json_writer.cpp
|
||||||
version.h.in
|
version.h.in)
|
||||||
)
|
|
||||||
SET_TARGET_PROPERTIES( jsoncpp_lib PROPERTIES OUTPUT_NAME jsoncpp )
|
|
||||||
SET_TARGET_PROPERTIES( jsoncpp_lib PROPERTIES VERSION ${JSONCPP_VERSION} SOVERSION ${JSONCPP_VERSION_MAJOR} )
|
|
||||||
|
|
||||||
IF(NOT CMAKE_VERSION VERSION_LESS 2.8.11)
|
|
||||||
TARGET_INCLUDE_DIRECTORIES( jsoncpp_lib PUBLIC
|
|
||||||
$<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>
|
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${JSONCPP_INCLUDE_DIR}>
|
|
||||||
)
|
|
||||||
ENDIF(NOT CMAKE_VERSION VERSION_LESS 2.8.11)
|
|
||||||
|
|
||||||
# Install instructions for this target
|
# Install instructions for this target
|
||||||
IF(JSONCPP_WITH_CMAKE_PACKAGE)
|
IF(JSONCPP_WITH_CMAKE_PACKAGE)
|
||||||
@@ -63,8 +47,40 @@ ELSE(JSONCPP_WITH_CMAKE_PACKAGE)
|
|||||||
SET(INSTALL_EXPORT)
|
SET(INSTALL_EXPORT)
|
||||||
ENDIF(JSONCPP_WITH_CMAKE_PACKAGE)
|
ENDIF(JSONCPP_WITH_CMAKE_PACKAGE)
|
||||||
|
|
||||||
|
IF(JSONCPP_LIB_BUILD_SHARED)
|
||||||
|
ADD_DEFINITIONS( -DJSON_DLL_BUILD )
|
||||||
|
ADD_LIBRARY(jsoncpp_lib SHARED ${PUBLIC_HEADERS} ${jsoncpp_sources})
|
||||||
|
SET_TARGET_PROPERTIES( jsoncpp_lib PROPERTIES VERSION ${JSONCPP_VERSION} SOVERSION ${JSONCPP_VERSION_MAJOR})
|
||||||
|
SET_TARGET_PROPERTIES( jsoncpp_lib PROPERTIES OUTPUT_NAME jsoncpp )
|
||||||
|
|
||||||
INSTALL( TARGETS jsoncpp_lib ${INSTALL_EXPORT}
|
INSTALL( TARGETS jsoncpp_lib ${INSTALL_EXPORT}
|
||||||
RUNTIME DESTINATION ${RUNTIME_INSTALL_DIR}
|
RUNTIME DESTINATION ${RUNTIME_INSTALL_DIR}
|
||||||
LIBRARY DESTINATION ${LIBRARY_INSTALL_DIR}
|
LIBRARY DESTINATION ${LIBRARY_INSTALL_DIR}
|
||||||
ARCHIVE DESTINATION ${ARCHIVE_INSTALL_DIR}
|
ARCHIVE DESTINATION ${ARCHIVE_INSTALL_DIR})
|
||||||
|
|
||||||
|
IF(NOT CMAKE_VERSION VERSION_LESS 2.8.11)
|
||||||
|
TARGET_INCLUDE_DIRECTORIES( jsoncpp_lib PUBLIC
|
||||||
|
$<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${JSONCPP_INCLUDE_DIR}>)
|
||||||
|
ENDIF(NOT CMAKE_VERSION VERSION_LESS 2.8.11)
|
||||||
|
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(JSONCPP_LIB_BUILD_STATIC)
|
||||||
|
ADD_LIBRARY(jsoncpp_lib_static STATIC ${PUBLIC_HEADERS} ${jsoncpp_sources})
|
||||||
|
SET_TARGET_PROPERTIES( jsoncpp_lib_static PROPERTIES VERSION ${JSONCPP_VERSION} SOVERSION ${JSONCPP_VERSION_MAJOR})
|
||||||
|
SET_TARGET_PROPERTIES( jsoncpp_lib_static PROPERTIES OUTPUT_NAME jsoncpp )
|
||||||
|
|
||||||
|
INSTALL( TARGETS jsoncpp_lib_static ${INSTALL_EXPORT}
|
||||||
|
RUNTIME DESTINATION ${RUNTIME_INSTALL_DIR}
|
||||||
|
LIBRARY DESTINATION ${LIBRARY_INSTALL_DIR}
|
||||||
|
ARCHIVE DESTINATION ${ARCHIVE_INSTALL_DIR})
|
||||||
|
|
||||||
|
IF(NOT CMAKE_VERSION VERSION_LESS 2.8.11)
|
||||||
|
TARGET_INCLUDE_DIRECTORIES( jsoncpp_lib_static PUBLIC
|
||||||
|
$<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${JSONCPP_INCLUDE_DIR}>
|
||||||
)
|
)
|
||||||
|
ENDIF(NOT CMAKE_VERSION VERSION_LESS 2.8.11)
|
||||||
|
|
||||||
|
ENDIF()
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,7 @@ namespace Json {
|
|||||||
static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
|
static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
|
||||||
const unsigned char& kNullRef = kNull[0];
|
const unsigned char& kNullRef = kNull[0];
|
||||||
const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);
|
const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);
|
||||||
|
const Value& Value::nullRef = null;
|
||||||
|
|
||||||
const Int Value::minInt = Int(~(UInt(-1) / 2));
|
const Int Value::minInt = Int(~(UInt(-1) / 2));
|
||||||
const Int Value::maxInt = Int(UInt(-1) / 2);
|
const Int Value::maxInt = Int(UInt(-1) / 2);
|
||||||
@@ -141,15 +142,17 @@ Value::CommentInfo::~CommentInfo() {
|
|||||||
releaseStringValue(comment_);
|
releaseStringValue(comment_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Value::CommentInfo::setComment(const char* text) {
|
void Value::CommentInfo::setComment(const char* text, size_t len) {
|
||||||
if (comment_)
|
if (comment_) {
|
||||||
releaseStringValue(comment_);
|
releaseStringValue(comment_);
|
||||||
|
comment_ = 0;
|
||||||
|
}
|
||||||
JSON_ASSERT(text != 0);
|
JSON_ASSERT(text != 0);
|
||||||
JSON_ASSERT_MESSAGE(
|
JSON_ASSERT_MESSAGE(
|
||||||
text[0] == '\0' || text[0] == '/',
|
text[0] == '\0' || text[0] == '/',
|
||||||
"in Json::Value::setComment(): Comments must start with /");
|
"in Json::Value::setComment(): Comments must start with /");
|
||||||
// It seems that /**/ style comments are acceptable as well.
|
// It seems that /**/ style comments are acceptable as well.
|
||||||
comment_ = duplicateStringValue(text);
|
comment_ = duplicateStringValue(text, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////
|
||||||
@@ -340,7 +343,7 @@ Value::Value(const Value& other)
|
|||||||
case stringValue:
|
case stringValue:
|
||||||
if (other.value_.string_) {
|
if (other.value_.string_) {
|
||||||
value_.string_ = duplicateStringValue(other.value_.string_);
|
value_.string_ = duplicateStringValue(other.value_.string_);
|
||||||
allocated_ |= true;
|
allocated_ = true;
|
||||||
} else {
|
} else {
|
||||||
value_.string_ = 0;
|
value_.string_ = 0;
|
||||||
allocated_ = false;
|
allocated_ = false;
|
||||||
@@ -367,7 +370,8 @@ Value::Value(const Value& other)
|
|||||||
for (int comment = 0; comment < numberOfCommentPlacement; ++comment) {
|
for (int comment = 0; comment < numberOfCommentPlacement; ++comment) {
|
||||||
const CommentInfo& otherComment = other.comments_[comment];
|
const CommentInfo& otherComment = other.comments_[comment];
|
||||||
if (otherComment.comment_)
|
if (otherComment.comment_)
|
||||||
comments_[comment].setComment(otherComment.comment_);
|
comments_[comment].setComment(
|
||||||
|
otherComment.comment_, strlen(otherComment.comment_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1225,14 +1229,22 @@ bool Value::isArray() const { return type_ == arrayValue; }
|
|||||||
|
|
||||||
bool Value::isObject() const { return type_ == objectValue; }
|
bool Value::isObject() const { return type_ == objectValue; }
|
||||||
|
|
||||||
void Value::setComment(const char* comment, CommentPlacement placement) {
|
void Value::setComment(const char* comment, size_t len, CommentPlacement placement) {
|
||||||
if (!comments_)
|
if (!comments_)
|
||||||
comments_ = new CommentInfo[numberOfCommentPlacement];
|
comments_ = new CommentInfo[numberOfCommentPlacement];
|
||||||
comments_[placement].setComment(comment);
|
if ((len > 0) && (comment[len-1] == '\n')) {
|
||||||
|
// Always discard trailing newline, to aid indentation.
|
||||||
|
len -= 1;
|
||||||
|
}
|
||||||
|
comments_[placement].setComment(comment, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Value::setComment(const char* comment, CommentPlacement placement) {
|
||||||
|
setComment(comment, strlen(comment), placement);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Value::setComment(const std::string& comment, CommentPlacement placement) {
|
void Value::setComment(const std::string& comment, CommentPlacement placement) {
|
||||||
setComment(comment.c_str(), placement);
|
setComment(comment.c_str(), comment.length(), placement);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Value::hasComment(CommentPlacement placement) const {
|
bool Value::hasComment(CommentPlacement placement) const {
|
||||||
|
@@ -7,13 +7,16 @@
|
|||||||
#include <json/writer.h>
|
#include <json/writer.h>
|
||||||
#include "json_tool.h"
|
#include "json_tool.h"
|
||||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||||
|
#include <iomanip>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <set>
|
||||||
|
#include <stdexcept>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sstream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
|
#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
@@ -33,6 +36,12 @@
|
|||||||
|
|
||||||
namespace Json {
|
namespace Json {
|
||||||
|
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
|
||||||
|
#else
|
||||||
|
typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
|
||||||
|
#endif
|
||||||
|
|
||||||
static bool containsControlCharacter(const char* str) {
|
static bool containsControlCharacter(const char* str) {
|
||||||
while (*str) {
|
while (*str) {
|
||||||
if (isControlCharacter(*(str++)))
|
if (isControlCharacter(*(str++)))
|
||||||
@@ -376,6 +385,9 @@ bool StyledWriter::isMultineArray(const Value& value) {
|
|||||||
addChildValues_ = true;
|
addChildValues_ = true;
|
||||||
int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
||||||
for (int index = 0; index < size; ++index) {
|
for (int index = 0; index < size; ++index) {
|
||||||
|
if (hasCommentForValue(value[index])) {
|
||||||
|
isMultiLine = true;
|
||||||
|
}
|
||||||
writeValue(value[index]);
|
writeValue(value[index]);
|
||||||
lineLength += int(childValues_[index].length());
|
lineLength += int(childValues_[index].length());
|
||||||
}
|
}
|
||||||
@@ -463,7 +475,10 @@ void StyledStreamWriter::write(std::ostream& out, const Value& root) {
|
|||||||
document_ = &out;
|
document_ = &out;
|
||||||
addChildValues_ = false;
|
addChildValues_ = false;
|
||||||
indentString_ = "";
|
indentString_ = "";
|
||||||
|
indented_ = true;
|
||||||
writeCommentBeforeValue(root);
|
writeCommentBeforeValue(root);
|
||||||
|
if (!indented_) writeIndent();
|
||||||
|
indented_ = true;
|
||||||
writeValue(root);
|
writeValue(root);
|
||||||
writeCommentAfterValueOnSameLine(root);
|
writeCommentAfterValueOnSameLine(root);
|
||||||
*document_ << "\n";
|
*document_ << "\n";
|
||||||
@@ -539,8 +554,10 @@ void StyledStreamWriter::writeArrayValue(const Value& value) {
|
|||||||
if (hasChildValue)
|
if (hasChildValue)
|
||||||
writeWithIndent(childValues_[index]);
|
writeWithIndent(childValues_[index]);
|
||||||
else {
|
else {
|
||||||
writeIndent();
|
if (!indented_) writeIndent();
|
||||||
|
indented_ = true;
|
||||||
writeValue(childValue);
|
writeValue(childValue);
|
||||||
|
indented_ = false;
|
||||||
}
|
}
|
||||||
if (++index == size) {
|
if (++index == size) {
|
||||||
writeCommentAfterValueOnSameLine(childValue);
|
writeCommentAfterValueOnSameLine(childValue);
|
||||||
@@ -581,6 +598,9 @@ bool StyledStreamWriter::isMultineArray(const Value& value) {
|
|||||||
addChildValues_ = true;
|
addChildValues_ = true;
|
||||||
int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
||||||
for (int index = 0; index < size; ++index) {
|
for (int index = 0; index < size; ++index) {
|
||||||
|
if (hasCommentForValue(value[index])) {
|
||||||
|
isMultiLine = true;
|
||||||
|
}
|
||||||
writeValue(value[index]);
|
writeValue(value[index]);
|
||||||
lineLength += int(childValues_[index].length());
|
lineLength += int(childValues_[index].length());
|
||||||
}
|
}
|
||||||
@@ -598,24 +618,17 @@ void StyledStreamWriter::pushValue(const std::string& value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void StyledStreamWriter::writeIndent() {
|
void StyledStreamWriter::writeIndent() {
|
||||||
/*
|
// blep intended this to look at the so-far-written string
|
||||||
Some comments in this method would have been nice. ;-)
|
// to determine whether we are already indented, but
|
||||||
|
// with a stream we cannot do that. So we rely on some saved state.
|
||||||
if ( !document_.empty() )
|
// The caller checks indented_.
|
||||||
{
|
|
||||||
char last = document_[document_.length()-1];
|
|
||||||
if ( last == ' ' ) // already indented
|
|
||||||
return;
|
|
||||||
if ( last != '\n' ) // Comments may add new-line
|
|
||||||
*document_ << '\n';
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
*document_ << '\n' << indentString_;
|
*document_ << '\n' << indentString_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StyledStreamWriter::writeWithIndent(const std::string& value) {
|
void StyledStreamWriter::writeWithIndent(const std::string& value) {
|
||||||
writeIndent();
|
if (!indented_) writeIndent();
|
||||||
*document_ << value;
|
*document_ << value;
|
||||||
|
indented_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StyledStreamWriter::indent() { indentString_ += indentation_; }
|
void StyledStreamWriter::indent() { indentString_ += indentation_; }
|
||||||
@@ -628,19 +641,30 @@ void StyledStreamWriter::unindent() {
|
|||||||
void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
|
void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
|
||||||
if (!root.hasComment(commentBefore))
|
if (!root.hasComment(commentBefore))
|
||||||
return;
|
return;
|
||||||
*document_ << root.getComment(commentBefore);
|
|
||||||
*document_ << "\n";
|
if (!indented_) writeIndent();
|
||||||
|
const std::string& comment = root.getComment(commentBefore);
|
||||||
|
std::string::const_iterator iter = comment.begin();
|
||||||
|
while (iter != comment.end()) {
|
||||||
|
*document_ << *iter;
|
||||||
|
if (*iter == '\n' &&
|
||||||
|
(iter != comment.end() && *(iter + 1) == '/'))
|
||||||
|
// writeIndent(); // would include newline
|
||||||
|
*document_ << indentString_;
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
indented_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
|
void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
|
||||||
if (root.hasComment(commentAfterOnSameLine))
|
if (root.hasComment(commentAfterOnSameLine))
|
||||||
*document_ << " " + root.getComment(commentAfterOnSameLine);
|
*document_ << ' ' << root.getComment(commentAfterOnSameLine);
|
||||||
|
|
||||||
if (root.hasComment(commentAfter)) {
|
if (root.hasComment(commentAfter)) {
|
||||||
*document_ << "\n";
|
writeIndent();
|
||||||
*document_ << root.getComment(commentAfter);
|
*document_ << root.getComment(commentAfter);
|
||||||
*document_ << "\n";
|
|
||||||
}
|
}
|
||||||
|
indented_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StyledStreamWriter::hasCommentForValue(const Value& value) {
|
bool StyledStreamWriter::hasCommentForValue(const Value& value) {
|
||||||
@@ -649,9 +673,376 @@ bool StyledStreamWriter::hasCommentForValue(const Value& value) {
|
|||||||
value.hasComment(commentAfter);
|
value.hasComment(commentAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& sout, const Value& root) {
|
//////////////////////////
|
||||||
Json::StyledStreamWriter writer;
|
// BuiltStyledStreamWriter
|
||||||
writer.write(sout, root);
|
|
||||||
|
/// Scoped enums are not available until C++11.
|
||||||
|
struct CommentStyle {
|
||||||
|
/// Decide whether to write comments.
|
||||||
|
enum Enum {
|
||||||
|
None, ///< Drop all comments.
|
||||||
|
Most, ///< Recover odd behavior of previous versions (not implemented yet).
|
||||||
|
All ///< Keep all comments.
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuiltStyledStreamWriter : public StreamWriter
|
||||||
|
{
|
||||||
|
BuiltStyledStreamWriter(
|
||||||
|
std::string const& indentation,
|
||||||
|
CommentStyle::Enum cs,
|
||||||
|
std::string const& colonSymbol,
|
||||||
|
std::string const& nullSymbol,
|
||||||
|
std::string const& endingLineFeedSymbol);
|
||||||
|
virtual int write(Value const& root, std::ostream* sout);
|
||||||
|
private:
|
||||||
|
void writeValue(Value const& value);
|
||||||
|
void writeArrayValue(Value const& value);
|
||||||
|
bool isMultineArray(Value const& value);
|
||||||
|
void pushValue(std::string const& value);
|
||||||
|
void writeIndent();
|
||||||
|
void writeWithIndent(std::string const& value);
|
||||||
|
void indent();
|
||||||
|
void unindent();
|
||||||
|
void writeCommentBeforeValue(Value const& root);
|
||||||
|
void writeCommentAfterValueOnSameLine(Value const& root);
|
||||||
|
static bool hasCommentForValue(const Value& value);
|
||||||
|
|
||||||
|
typedef std::vector<std::string> ChildValues;
|
||||||
|
|
||||||
|
ChildValues childValues_;
|
||||||
|
std::string indentString_;
|
||||||
|
int rightMargin_;
|
||||||
|
std::string indentation_;
|
||||||
|
CommentStyle::Enum cs_;
|
||||||
|
std::string colonSymbol_;
|
||||||
|
std::string nullSymbol_;
|
||||||
|
std::string endingLineFeedSymbol_;
|
||||||
|
bool addChildValues_ : 1;
|
||||||
|
bool indented_ : 1;
|
||||||
|
};
|
||||||
|
BuiltStyledStreamWriter::BuiltStyledStreamWriter(
|
||||||
|
std::string const& indentation,
|
||||||
|
CommentStyle::Enum cs,
|
||||||
|
std::string const& colonSymbol,
|
||||||
|
std::string const& nullSymbol,
|
||||||
|
std::string const& endingLineFeedSymbol)
|
||||||
|
: rightMargin_(74)
|
||||||
|
, indentation_(indentation)
|
||||||
|
, cs_(cs)
|
||||||
|
, colonSymbol_(colonSymbol)
|
||||||
|
, nullSymbol_(nullSymbol)
|
||||||
|
, endingLineFeedSymbol_(endingLineFeedSymbol)
|
||||||
|
, addChildValues_(false)
|
||||||
|
, indented_(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout)
|
||||||
|
{
|
||||||
|
sout_ = sout;
|
||||||
|
addChildValues_ = false;
|
||||||
|
indented_ = true;
|
||||||
|
indentString_ = "";
|
||||||
|
writeCommentBeforeValue(root);
|
||||||
|
if (!indented_) writeIndent();
|
||||||
|
indented_ = true;
|
||||||
|
writeValue(root);
|
||||||
|
writeCommentAfterValueOnSameLine(root);
|
||||||
|
*sout_ << endingLineFeedSymbol_;
|
||||||
|
sout_ = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
void BuiltStyledStreamWriter::writeValue(Value const& value) {
|
||||||
|
switch (value.type()) {
|
||||||
|
case nullValue:
|
||||||
|
pushValue(nullSymbol_);
|
||||||
|
break;
|
||||||
|
case intValue:
|
||||||
|
pushValue(valueToString(value.asLargestInt()));
|
||||||
|
break;
|
||||||
|
case uintValue:
|
||||||
|
pushValue(valueToString(value.asLargestUInt()));
|
||||||
|
break;
|
||||||
|
case realValue:
|
||||||
|
pushValue(valueToString(value.asDouble()));
|
||||||
|
break;
|
||||||
|
case stringValue:
|
||||||
|
pushValue(valueToQuotedString(value.asCString()));
|
||||||
|
break;
|
||||||
|
case booleanValue:
|
||||||
|
pushValue(valueToString(value.asBool()));
|
||||||
|
break;
|
||||||
|
case arrayValue:
|
||||||
|
writeArrayValue(value);
|
||||||
|
break;
|
||||||
|
case objectValue: {
|
||||||
|
Value::Members members(value.getMemberNames());
|
||||||
|
if (members.empty())
|
||||||
|
pushValue("{}");
|
||||||
|
else {
|
||||||
|
writeWithIndent("{");
|
||||||
|
indent();
|
||||||
|
Value::Members::iterator it = members.begin();
|
||||||
|
for (;;) {
|
||||||
|
std::string const& name = *it;
|
||||||
|
Value const& childValue = value[name];
|
||||||
|
writeCommentBeforeValue(childValue);
|
||||||
|
writeWithIndent(valueToQuotedString(name.c_str()));
|
||||||
|
*sout_ << colonSymbol_;
|
||||||
|
writeValue(childValue);
|
||||||
|
if (++it == members.end()) {
|
||||||
|
writeCommentAfterValueOnSameLine(childValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*sout_ << ",";
|
||||||
|
writeCommentAfterValueOnSameLine(childValue);
|
||||||
|
}
|
||||||
|
unindent();
|
||||||
|
writeWithIndent("}");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
|
||||||
|
unsigned size = value.size();
|
||||||
|
if (size == 0)
|
||||||
|
pushValue("[]");
|
||||||
|
else {
|
||||||
|
bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value);
|
||||||
|
if (isMultiLine) {
|
||||||
|
writeWithIndent("[");
|
||||||
|
indent();
|
||||||
|
bool hasChildValue = !childValues_.empty();
|
||||||
|
unsigned index = 0;
|
||||||
|
for (;;) {
|
||||||
|
Value const& childValue = value[index];
|
||||||
|
writeCommentBeforeValue(childValue);
|
||||||
|
if (hasChildValue)
|
||||||
|
writeWithIndent(childValues_[index]);
|
||||||
|
else {
|
||||||
|
if (!indented_) writeIndent();
|
||||||
|
indented_ = true;
|
||||||
|
writeValue(childValue);
|
||||||
|
indented_ = false;
|
||||||
|
}
|
||||||
|
if (++index == size) {
|
||||||
|
writeCommentAfterValueOnSameLine(childValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*sout_ << ",";
|
||||||
|
writeCommentAfterValueOnSameLine(childValue);
|
||||||
|
}
|
||||||
|
unindent();
|
||||||
|
writeWithIndent("]");
|
||||||
|
} else // output on a single line
|
||||||
|
{
|
||||||
|
assert(childValues_.size() == size);
|
||||||
|
*sout_ << "[";
|
||||||
|
if (!indentation_.empty()) *sout_ << " ";
|
||||||
|
for (unsigned index = 0; index < size; ++index) {
|
||||||
|
if (index > 0)
|
||||||
|
*sout_ << ", ";
|
||||||
|
*sout_ << childValues_[index];
|
||||||
|
}
|
||||||
|
if (!indentation_.empty()) *sout_ << " ";
|
||||||
|
*sout_ << "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuiltStyledStreamWriter::isMultineArray(Value const& value) {
|
||||||
|
int size = value.size();
|
||||||
|
bool isMultiLine = size * 3 >= rightMargin_;
|
||||||
|
childValues_.clear();
|
||||||
|
for (int index = 0; index < size && !isMultiLine; ++index) {
|
||||||
|
Value const& childValue = value[index];
|
||||||
|
isMultiLine =
|
||||||
|
isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
|
||||||
|
childValue.size() > 0);
|
||||||
|
}
|
||||||
|
if (!isMultiLine) // check if line length > max line length
|
||||||
|
{
|
||||||
|
childValues_.reserve(size);
|
||||||
|
addChildValues_ = true;
|
||||||
|
int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
||||||
|
for (int index = 0; index < size; ++index) {
|
||||||
|
if (hasCommentForValue(value[index])) {
|
||||||
|
isMultiLine = true;
|
||||||
|
}
|
||||||
|
writeValue(value[index]);
|
||||||
|
lineLength += int(childValues_[index].length());
|
||||||
|
}
|
||||||
|
addChildValues_ = false;
|
||||||
|
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
||||||
|
}
|
||||||
|
return isMultiLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuiltStyledStreamWriter::pushValue(std::string const& value) {
|
||||||
|
if (addChildValues_)
|
||||||
|
childValues_.push_back(value);
|
||||||
|
else
|
||||||
|
*sout_ << value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuiltStyledStreamWriter::writeIndent() {
|
||||||
|
// blep intended this to look at the so-far-written string
|
||||||
|
// to determine whether we are already indented, but
|
||||||
|
// with a stream we cannot do that. So we rely on some saved state.
|
||||||
|
// The caller checks indented_.
|
||||||
|
|
||||||
|
if (!indentation_.empty()) {
|
||||||
|
// In this case, drop newlines too.
|
||||||
|
*sout_ << '\n' << indentString_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) {
|
||||||
|
if (!indented_) writeIndent();
|
||||||
|
*sout_ << value;
|
||||||
|
indented_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
|
||||||
|
|
||||||
|
void BuiltStyledStreamWriter::unindent() {
|
||||||
|
assert(indentString_.size() >= indentation_.size());
|
||||||
|
indentString_.resize(indentString_.size() - indentation_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
|
||||||
|
if (cs_ == CommentStyle::None) return;
|
||||||
|
if (!root.hasComment(commentBefore))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!indented_) writeIndent();
|
||||||
|
const std::string& comment = root.getComment(commentBefore);
|
||||||
|
std::string::const_iterator iter = comment.begin();
|
||||||
|
while (iter != comment.end()) {
|
||||||
|
*sout_ << *iter;
|
||||||
|
if (*iter == '\n' &&
|
||||||
|
(iter != comment.end() && *(iter + 1) == '/'))
|
||||||
|
// writeIndent(); // would write extra newline
|
||||||
|
*sout_ << indentString_;
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
indented_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) {
|
||||||
|
if (cs_ == CommentStyle::None) return;
|
||||||
|
if (root.hasComment(commentAfterOnSameLine))
|
||||||
|
*sout_ << " " + root.getComment(commentAfterOnSameLine);
|
||||||
|
|
||||||
|
if (root.hasComment(commentAfter)) {
|
||||||
|
writeIndent();
|
||||||
|
*sout_ << root.getComment(commentAfter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
|
||||||
|
return value.hasComment(commentBefore) ||
|
||||||
|
value.hasComment(commentAfterOnSameLine) ||
|
||||||
|
value.hasComment(commentAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////
|
||||||
|
// StreamWriter
|
||||||
|
|
||||||
|
StreamWriter::StreamWriter()
|
||||||
|
: sout_(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
StreamWriter::~StreamWriter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
StreamWriter::Factory::~Factory()
|
||||||
|
{}
|
||||||
|
StreamWriterBuilder::StreamWriterBuilder()
|
||||||
|
{
|
||||||
|
setDefaults(&settings_);
|
||||||
|
}
|
||||||
|
StreamWriterBuilder::~StreamWriterBuilder()
|
||||||
|
{}
|
||||||
|
StreamWriter* StreamWriterBuilder::newStreamWriter() const
|
||||||
|
{
|
||||||
|
std::string indentation = settings_["indentation"].asString();
|
||||||
|
std::string cs_str = settings_["commentStyle"].asString();
|
||||||
|
bool eyc = settings_["enableYAMLCompatibility"].asBool();
|
||||||
|
bool dnp = settings_["dropNullPlaceholders"].asBool();
|
||||||
|
CommentStyle::Enum cs = CommentStyle::All;
|
||||||
|
if (cs_str == "All") {
|
||||||
|
cs = CommentStyle::All;
|
||||||
|
} else if (cs_str == "None") {
|
||||||
|
cs = CommentStyle::None;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("commentStyle must be 'All' or 'None'");
|
||||||
|
}
|
||||||
|
std::string colonSymbol = " : ";
|
||||||
|
if (eyc) {
|
||||||
|
colonSymbol = ": ";
|
||||||
|
} else if (indentation.empty()) {
|
||||||
|
colonSymbol = ":";
|
||||||
|
}
|
||||||
|
std::string nullSymbol = "null";
|
||||||
|
if (dnp) {
|
||||||
|
nullSymbol = "";
|
||||||
|
}
|
||||||
|
std::string endingLineFeedSymbol = "";
|
||||||
|
return new BuiltStyledStreamWriter(
|
||||||
|
indentation, cs,
|
||||||
|
colonSymbol, nullSymbol, endingLineFeedSymbol);
|
||||||
|
}
|
||||||
|
static void getValidWriterKeys(std::set<std::string>* valid_keys)
|
||||||
|
{
|
||||||
|
valid_keys->clear();
|
||||||
|
valid_keys->insert("indentation");
|
||||||
|
valid_keys->insert("commentStyle");
|
||||||
|
valid_keys->insert("enableYAMLCompatibility");
|
||||||
|
valid_keys->insert("dropNullPlaceholders");
|
||||||
|
}
|
||||||
|
bool StreamWriterBuilder::validate(Json::Value* invalid) const
|
||||||
|
{
|
||||||
|
Json::Value my_invalid;
|
||||||
|
if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL
|
||||||
|
Json::Value& inv = *invalid;
|
||||||
|
bool valid = true;
|
||||||
|
std::set<std::string> valid_keys;
|
||||||
|
getValidWriterKeys(&valid_keys);
|
||||||
|
Value::Members keys = settings_.getMemberNames();
|
||||||
|
size_t n = keys.size();
|
||||||
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
std::string const& key = keys[i];
|
||||||
|
if (valid_keys.find(key) == valid_keys.end()) {
|
||||||
|
inv[key] = settings_[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
// static
|
||||||
|
void StreamWriterBuilder::setDefaults(Json::Value* settings)
|
||||||
|
{
|
||||||
|
//! [StreamWriterBuilderDefaults]
|
||||||
|
(*settings)["commentStyle"] = "All";
|
||||||
|
(*settings)["indentation"] = "\t";
|
||||||
|
(*settings)["enableYAMLCompatibility"] = false;
|
||||||
|
(*settings)["dropNullPlaceholders"] = false;
|
||||||
|
//! [StreamWriterBuilderDefaults]
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string writeString(StreamWriter::Factory const& builder, Value const& root) {
|
||||||
|
std::ostringstream sout;
|
||||||
|
StreamWriterPtr const writer(builder.newStreamWriter());
|
||||||
|
writer->write(root, &sout);
|
||||||
|
return sout.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& sout, Value const& root) {
|
||||||
|
StreamWriterBuilder builder;
|
||||||
|
StreamWriterPtr const writer(builder.newStreamWriter());
|
||||||
|
writer->write(root, &sout);
|
||||||
return sout;
|
return sout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,7 +9,15 @@ ADD_EXECUTABLE( jsoncpp_test
|
|||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
IF(JSONCPP_LIB_BUILD_SHARED)
|
||||||
TARGET_LINK_LIBRARIES(jsoncpp_test jsoncpp_lib)
|
TARGET_LINK_LIBRARIES(jsoncpp_test jsoncpp_lib)
|
||||||
|
ELSE(JSONCPP_LIB_BUILD_SHARED)
|
||||||
|
TARGET_LINK_LIBRARIES(jsoncpp_test jsoncpp_lib_static)
|
||||||
|
ENDIF(JSONCPP_LIB_BUILD_SHARED)
|
||||||
|
|
||||||
|
# another way to solve issue #90
|
||||||
|
#set_target_properties(jsoncpp_test PROPERTIES COMPILE_FLAGS -ffloat-store)
|
||||||
|
|
||||||
# Run unit tests in post-build
|
# Run unit tests in post-build
|
||||||
# (default cmake workflow hides away the test result into a file, resulting in poor dev workflow?!?)
|
# (default cmake workflow hides away the test result into a file, resulting in poor dev workflow?!?)
|
||||||
|
@@ -323,7 +323,7 @@ void Runner::listTests() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Runner::runCommandLine(int argc, const char* argv[]) const {
|
int Runner::runCommandLine(int argc, const char* argv[]) const {
|
||||||
typedef std::deque<std::string> TestNames;
|
// typedef std::deque<std::string> TestNames;
|
||||||
Runner subrunner;
|
Runner subrunner;
|
||||||
for (int index = 1; index < argc; ++index) {
|
for (int index = 1; index < argc; ++index) {
|
||||||
std::string opt = argv[index];
|
std::string opt = argv[index];
|
||||||
|
@@ -178,8 +178,8 @@ private:
|
|||||||
|
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
TestResult& checkEqual(TestResult& result,
|
TestResult& checkEqual(TestResult& result,
|
||||||
const T& expected,
|
T expected,
|
||||||
const U& actual,
|
U actual,
|
||||||
const char* file,
|
const char* file,
|
||||||
unsigned int line,
|
unsigned int line,
|
||||||
const char* expr) {
|
const char* expr) {
|
||||||
@@ -214,7 +214,7 @@ TestResult& checkStringEqual(TestResult& result,
|
|||||||
#define JSONTEST_ASSERT_PRED(expr) \
|
#define JSONTEST_ASSERT_PRED(expr) \
|
||||||
{ \
|
{ \
|
||||||
JsonTest::PredicateContext _minitest_Context = { \
|
JsonTest::PredicateContext _minitest_Context = { \
|
||||||
result_->predicateId_, __FILE__, __LINE__, #expr \
|
result_->predicateId_, __FILE__, __LINE__, #expr, NULL, NULL \
|
||||||
}; \
|
}; \
|
||||||
result_->predicateStackTail_->next_ = &_minitest_Context; \
|
result_->predicateStackTail_->next_ = &_minitest_Context; \
|
||||||
result_->predicateId_ += 1; \
|
result_->predicateId_ += 1; \
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
#include <json/config.h>
|
#include <json/config.h>
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
// Make numeric limits more convenient to talk about.
|
// Make numeric limits more convenient to talk about.
|
||||||
// Assumes int type in 32 bits.
|
// Assumes int type in 32 bits.
|
||||||
@@ -1526,6 +1527,17 @@ JSONTEST_FIXTURE(WriterTest, dropNullPlaceholders) {
|
|||||||
JSONTEST_ASSERT(writer.write(nullValue) == "\n");
|
JSONTEST_ASSERT(writer.write(nullValue) == "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct StreamWriterTest : JsonTest::TestCase {};
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE(StreamWriterTest, dropNullPlaceholders) {
|
||||||
|
Json::StreamWriterBuilder b;
|
||||||
|
Json::Value nullValue;
|
||||||
|
b.settings_["dropNullPlaceholders"] = false;
|
||||||
|
JSONTEST_ASSERT(Json::writeString(b, nullValue) == "null");
|
||||||
|
b.settings_["dropNullPlaceholders"] = true;
|
||||||
|
JSONTEST_ASSERT(Json::writeString(b, nullValue) == "");
|
||||||
|
}
|
||||||
|
|
||||||
struct ReaderTest : JsonTest::TestCase {};
|
struct ReaderTest : JsonTest::TestCase {};
|
||||||
|
|
||||||
JSONTEST_FIXTURE(ReaderTest, parseWithNoErrors) {
|
JSONTEST_FIXTURE(ReaderTest, parseWithNoErrors) {
|
||||||
@@ -1617,6 +1629,238 @@ JSONTEST_FIXTURE(ReaderTest, parseWithDetailError) {
|
|||||||
JSONTEST_ASSERT(errors.at(0).message == "Bad escape sequence in string");
|
JSONTEST_ASSERT(errors.at(0).message == "Bad escape sequence in string");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CharReaderTest : JsonTest::TestCase {};
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE(CharReaderTest, parseWithNoErrors) {
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
Json::Value root;
|
||||||
|
char const doc[] = "{ \"property\" : \"value\" }";
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(ok);
|
||||||
|
JSONTEST_ASSERT(errs.size() == 0);
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE(CharReaderTest, parseWithNoErrorsTestingOffsets) {
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
Json::Value root;
|
||||||
|
char const doc[] =
|
||||||
|
"{ \"property\" : [\"value\", \"value2\"], \"obj\" : "
|
||||||
|
"{ \"nested\" : 123, \"bool\" : true}, \"null\" : "
|
||||||
|
"null, \"false\" : false }";
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(ok);
|
||||||
|
JSONTEST_ASSERT(errs.size() == 0);
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE(CharReaderTest, parseWithOneError) {
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
Json::Value root;
|
||||||
|
char const doc[] =
|
||||||
|
"{ \"property\" :: \"value\" }";
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(!ok);
|
||||||
|
JSONTEST_ASSERT(errs ==
|
||||||
|
"* Line 1, Column 15\n Syntax error: value, object or array "
|
||||||
|
"expected.\n");
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE(CharReaderTest, parseChineseWithOneError) {
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
Json::Value root;
|
||||||
|
char const doc[] =
|
||||||
|
"{ \"pr佐藤erty\" :: \"value\" }";
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(!ok);
|
||||||
|
JSONTEST_ASSERT(errs ==
|
||||||
|
"* Line 1, Column 19\n Syntax error: value, object or array "
|
||||||
|
"expected.\n");
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE(CharReaderTest, parseWithDetailError) {
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
Json::Value root;
|
||||||
|
char const doc[] =
|
||||||
|
"{ \"property\" : \"v\\alue\" }";
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(!ok);
|
||||||
|
JSONTEST_ASSERT(errs ==
|
||||||
|
"* Line 1, Column 16\n Bad escape sequence in string\nSee "
|
||||||
|
"Line 1, Column 20 for detail.\n");
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE(CharReaderTest, parseWithStackLimit) {
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
Json::Value root;
|
||||||
|
char const doc[] =
|
||||||
|
"{ \"property\" : \"value\" }";
|
||||||
|
{
|
||||||
|
b.settings_["stackLimit"] = 2;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(ok);
|
||||||
|
JSONTEST_ASSERT(errs == "");
|
||||||
|
JSONTEST_ASSERT_EQUAL("value", root["property"]);
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
b.settings_["stackLimit"] = 1;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
JSONTEST_ASSERT_THROWS(reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs));
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CharReaderFailIfExtraTest : JsonTest::TestCase {};
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue164) {
|
||||||
|
// This is interpretted as a string value followed by a colon.
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
Json::Value root;
|
||||||
|
char const doc[] =
|
||||||
|
" \"property\" : \"value\" }";
|
||||||
|
{
|
||||||
|
b.settings_["failIfExtra"] = false;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(ok);
|
||||||
|
JSONTEST_ASSERT(errs == "");
|
||||||
|
JSONTEST_ASSERT_EQUAL("property", root);
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
b.settings_["failIfExtra"] = true;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(!ok);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL(errs,
|
||||||
|
"* Line 1, Column 13\n"
|
||||||
|
" Extra non-whitespace after JSON value.\n");
|
||||||
|
JSONTEST_ASSERT_EQUAL("property", root);
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
b.settings_["failIfExtra"] = false;
|
||||||
|
b.strictMode(&b.settings_);
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(!ok);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL(errs,
|
||||||
|
"* Line 1, Column 13\n"
|
||||||
|
" Extra non-whitespace after JSON value.\n");
|
||||||
|
JSONTEST_ASSERT_EQUAL("property", root);
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue107) {
|
||||||
|
// This is interpretted as an int value followed by a colon.
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
Json::Value root;
|
||||||
|
char const doc[] =
|
||||||
|
"1:2:3";
|
||||||
|
b.settings_["failIfExtra"] = true;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(!ok);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL(
|
||||||
|
"* Line 1, Column 2\n"
|
||||||
|
" Extra non-whitespace after JSON value.\n",
|
||||||
|
errs);
|
||||||
|
JSONTEST_ASSERT_EQUAL(1, root.asInt());
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
|
JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterObject) {
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
Json::Value root;
|
||||||
|
{
|
||||||
|
char const doc[] =
|
||||||
|
"{ \"property\" : \"value\" } //trailing\n//comment\n";
|
||||||
|
b.settings_["failIfExtra"] = true;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(ok);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL("", errs);
|
||||||
|
JSONTEST_ASSERT_EQUAL("value", root["property"]);
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterArray) {
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
Json::Value root;
|
||||||
|
char const doc[] =
|
||||||
|
"[ \"property\" , \"value\" ] //trailing\n//comment\n";
|
||||||
|
b.settings_["failIfExtra"] = true;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(ok);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL("", errs);
|
||||||
|
JSONTEST_ASSERT_EQUAL("value", root[1u]);
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
|
JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterBool) {
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
Json::Value root;
|
||||||
|
char const doc[] =
|
||||||
|
" true /*trailing\ncomment*/";
|
||||||
|
b.settings_["failIfExtra"] = true;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
std::string errs;
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(ok);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL("", errs);
|
||||||
|
JSONTEST_ASSERT_EQUAL(true, root.asBool());
|
||||||
|
delete reader;
|
||||||
|
}
|
||||||
int main(int argc, const char* argv[]) {
|
int main(int argc, const char* argv[]) {
|
||||||
JsonTest::Runner runner;
|
JsonTest::Runner runner;
|
||||||
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr);
|
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr);
|
||||||
@@ -1647,7 +1891,22 @@ int main(int argc, const char* argv[]) {
|
|||||||
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseChineseWithOneError);
|
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseChineseWithOneError);
|
||||||
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithDetailError);
|
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithDetailError);
|
||||||
|
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithNoErrors);
|
||||||
|
JSONTEST_REGISTER_FIXTURE(
|
||||||
|
runner, CharReaderTest, parseWithNoErrorsTestingOffsets);
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithOneError);
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseChineseWithOneError);
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithDetailError);
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithStackLimit);
|
||||||
|
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, issue164);
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, issue107);
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterObject);
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterArray);
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterBool);
|
||||||
|
|
||||||
JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders);
|
JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders);
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, dropNullPlaceholders);
|
||||||
|
|
||||||
return runner.runCommandLine(argc, argv);
|
return runner.runCommandLine(argc, argv);
|
||||||
}
|
}
|
||||||
|
4
test/data/test_comment_00.expected
Normal file
4
test/data/test_comment_00.expected
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// Comment for array
|
||||||
|
.=[]
|
||||||
|
// Comment within array
|
||||||
|
.[0]="one-element"
|
5
test/data/test_comment_00.json
Normal file
5
test/data/test_comment_00.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Comment for array
|
||||||
|
[
|
||||||
|
// Comment within array
|
||||||
|
"one-element"
|
||||||
|
]
|
@@ -14,6 +14,7 @@ def getStatusOutput(cmd):
|
|||||||
Return int, unicode (for both Python 2 and 3).
|
Return int, unicode (for both Python 2 and 3).
|
||||||
Note: os.popen().close() would return None for 0.
|
Note: os.popen().close() would return None for 0.
|
||||||
"""
|
"""
|
||||||
|
print(cmd, file=sys.stderr)
|
||||||
pipe = os.popen(cmd)
|
pipe = os.popen(cmd)
|
||||||
process_output = pipe.read()
|
process_output = pipe.read()
|
||||||
try:
|
try:
|
||||||
@@ -57,7 +58,8 @@ def safeReadFile( path ):
|
|||||||
return '<File "%s" is missing: %s>' % (path,e)
|
return '<File "%s" is missing: %s>' % (path,e)
|
||||||
|
|
||||||
def runAllTests(jsontest_executable_path, input_dir = None,
|
def runAllTests(jsontest_executable_path, input_dir = None,
|
||||||
use_valgrind=False, with_json_checker=False ):
|
use_valgrind=False, with_json_checker=False,
|
||||||
|
writerClass='StyledWriter'):
|
||||||
if not input_dir:
|
if not input_dir:
|
||||||
input_dir = os.path.join(os.getcwd(), 'data')
|
input_dir = os.path.join(os.getcwd(), 'data')
|
||||||
tests = glob(os.path.join(input_dir, '*.json'))
|
tests = glob(os.path.join(input_dir, '*.json'))
|
||||||
@@ -72,8 +74,8 @@ def runAllTests( jsontest_executable_path, input_dir = None,
|
|||||||
is_json_checker_test = (input_path in test_jsonchecker) or expect_failure
|
is_json_checker_test = (input_path in test_jsonchecker) or expect_failure
|
||||||
print('TESTING:', input_path, end=' ')
|
print('TESTING:', input_path, end=' ')
|
||||||
options = is_json_checker_test and '--json-checker' or ''
|
options = is_json_checker_test and '--json-checker' or ''
|
||||||
cmd = '%s%s %s "%s"' % (
|
options += ' --json-writer %s'%writerClass
|
||||||
valgrind_path, jsontest_executable_path, options,
|
cmd = '%s%s %s "%s"' % ( valgrind_path, jsontest_executable_path, options,
|
||||||
input_path)
|
input_path)
|
||||||
status, process_output = getStatusOutput(cmd)
|
status, process_output = getStatusOutput(cmd)
|
||||||
if is_json_checker_test:
|
if is_json_checker_test:
|
||||||
@@ -145,7 +147,22 @@ def main():
|
|||||||
else:
|
else:
|
||||||
input_path = None
|
input_path = None
|
||||||
status = runAllTests(jsontest_executable_path, input_path,
|
status = runAllTests(jsontest_executable_path, input_path,
|
||||||
use_valgrind=options.valgrind, with_json_checker=options.with_json_checker )
|
use_valgrind=options.valgrind,
|
||||||
|
with_json_checker=options.with_json_checker,
|
||||||
|
writerClass='StyledWriter')
|
||||||
|
if status:
|
||||||
|
sys.exit(status)
|
||||||
|
status = runAllTests(jsontest_executable_path, input_path,
|
||||||
|
use_valgrind=options.valgrind,
|
||||||
|
with_json_checker=options.with_json_checker,
|
||||||
|
writerClass='StyledStreamWriter')
|
||||||
|
if status:
|
||||||
|
sys.exit(status)
|
||||||
|
status = runAllTests(jsontest_executable_path, input_path,
|
||||||
|
use_valgrind=options.valgrind,
|
||||||
|
with_json_checker=options.with_json_checker,
|
||||||
|
writerClass='BuiltStyledStreamWriter')
|
||||||
|
if status:
|
||||||
sys.exit(status)
|
sys.exit(status)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@@ -53,8 +53,7 @@ def runAllTests( exe_path, use_valgrind=False ):
|
|||||||
print()
|
print()
|
||||||
for name, result in failures:
|
for name, result in failures:
|
||||||
print(result)
|
print(result)
|
||||||
print('%d/%d tests passed (%d failure(s))' % (
|
print('%d/%d tests passed (%d failure(s))' % ( pass_count, len(test_names), failed_count))
|
||||||
pass_count, len(test_names), failed_count))
|
|
||||||
return 1
|
return 1
|
||||||
else:
|
else:
|
||||||
print('All %d tests passed' % len(test_names))
|
print('All %d tests passed' % len(test_names))
|
||||||
|
Reference in New Issue
Block a user