----
-# BasedOnStyle: LLVM
-AccessModifierOffset: -2
-ConstructorInitializerIndentWidth: 4
-AlignEscapedNewlinesLeft: false
-AlignTrailingComments: true
-AllowAllParametersOfDeclarationOnNextLine: true
-AllowShortIfStatementsOnASingleLine: false
-AllowShortLoopsOnASingleLine: false
-AlwaysBreakTemplateDeclarations: false
-AlwaysBreakBeforeMultilineStrings: false
-BreakBeforeBinaryOperators: false
-BreakBeforeTernaryOperators: true
-BreakConstructorInitializersBeforeComma: false
-BinPackParameters: false
-ColumnLimit: 80
-ConstructorInitializerAllOnOneLineOrOnePerLine: false
-DerivePointerBinding: false
-ExperimentalAutoDetectBinPacking: false
-IndentCaseLabels: false
-MaxEmptyLinesToKeep: 1
-NamespaceIndentation: None
-ObjCSpaceBeforeProtocolList: true
-PenaltyBreakBeforeFirstCallParameter: 19
-PenaltyBreakComment: 60
-PenaltyBreakString: 1000
-PenaltyBreakFirstLessLess: 120
-PenaltyExcessCharacter: 1000000
-PenaltyReturnTypeOnItsOwnLine: 60
-PointerBindsToType: true
-SpacesBeforeTrailingComments: 1
-Cpp11BracedListStyle: true
-Standard: Cpp11
-IndentWidth: 2
-TabWidth: 8
-UseTab: Never
-BreakBeforeBraces: Attach
-IndentFunctionDeclarationAfterType: false
-SpacesInParentheses: false
-SpacesInAngles: false
-SpaceInEmptyParentheses: false
-SpacesInCStyleCastParentheses: false
-SpaceAfterControlStatementKeyword: true
-SpaceBeforeAssignmentOperators: true
-ContinuationIndentWidth: 4
-...
+BasedOnStyle: LLVM
+DerivePointerAlignment: false
+PointerAlignment: Left
--- /dev/null
+---
+Checks: 'google-readability-casting,modernize-use-default-member-init,modernize-use-using,readability-redundant-member-init'
+WarningsAsErrors: ''
+HeaderFilterRegex: ''
+AnalyzeTemporaryDtors: false
+FormatStyle: none
+CheckOptions:
+ - key: modernize-use-using.IgnoreMacros
+ value: '0'
+...
+
/libs/
/doc/doxyfile
/dist/
-#/version
-#/include/json/version.h
# MSVC project files:
*.sln
addons:
homebrew:
packages:
+ - clang-format
- meson
- ninja
update: false # do not update homebrew by default
- ubuntu-toolchain-r-test
- llvm-toolchain-xenial-8
packages:
+ - clang-format-8
- clang-8
- valgrind
matrix:
include:
- name: Mac clang meson static release testing
os: osx
- osx_image: xcode10.2
+ osx_image: xcode11
compiler: clang
env:
CXX="clang++"
BUILD_TYPE=Debug
LIB_TYPE=shared
DESTDIR=/tmp/cmake_json_cpp
+ before_install:
+ - pip install --user cpp-coveralls
script: ./.travis_scripts/cmake_builder.sh
+ after_success:
+ - coveralls --include src/lib_json --include include
notifications:
email: false
ninja --version
_COMPILER_NAME=`basename ${CXX}`
_BUILD_DIR_NAME="build-${BUILD_TYPE}_${LIB_TYPE}_${_COMPILER_NAME}"
-meson --buildtype ${BUILD_TYPE} --default-library ${LIB_TYPE} . "${_BUILD_DIR_NAME}"
+
+./.travis_scripts/run-clang-format.sh
+meson --fatal-meson-warnings --werror --buildtype ${BUILD_TYPE} --default-library ${LIB_TYPE} . "${_BUILD_DIR_NAME}"
ninja -v -j 2 -C "${_BUILD_DIR_NAME}"
-#ninja -v -j 2 -C "${_BUILD_DIR_NAME}" test
+
cd "${_BUILD_DIR_NAME}"
meson test --no-rebuild --print-errorlogs
--- /dev/null
+#!/usr/bin/env python
+"""A wrapper script around clang-format, suitable for linting multiple files
+and to use for continuous integration.
+This is an alternative API for the clang-format command line.
+It runs over multiple files and directories in parallel.
+A diff output is produced and a sensible exit code is returned.
+
+NOTE: pulled from https://github.com/Sarcasm/run-clang-format, which is
+licensed under the MIT license.
+"""
+
+from __future__ import print_function, unicode_literals
+
+import argparse
+import codecs
+import difflib
+import fnmatch
+import io
+import multiprocessing
+import os
+import signal
+import subprocess
+import sys
+import traceback
+
+from functools import partial
+
+try:
+ from subprocess import DEVNULL # py3k
+except ImportError:
+ DEVNULL = open(os.devnull, "wb")
+
+
+DEFAULT_EXTENSIONS = 'c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx'
+
+
+class ExitStatus:
+ SUCCESS = 0
+ DIFF = 1
+ TROUBLE = 2
+
+
+def list_files(files, recursive=False, extensions=None, exclude=None):
+ if extensions is None:
+ extensions = []
+ if exclude is None:
+ exclude = []
+
+ out = []
+ for file in files:
+ if recursive and os.path.isdir(file):
+ for dirpath, dnames, fnames in os.walk(file):
+ fpaths = [os.path.join(dirpath, fname) for fname in fnames]
+ for pattern in exclude:
+ # os.walk() supports trimming down the dnames list
+ # by modifying it in-place,
+ # to avoid unnecessary directory listings.
+ dnames[:] = [
+ x for x in dnames
+ if
+ not fnmatch.fnmatch(os.path.join(dirpath, x), pattern)
+ ]
+ fpaths = [
+ x for x in fpaths if not fnmatch.fnmatch(x, pattern)
+ ]
+ for f in fpaths:
+ ext = os.path.splitext(f)[1][1:]
+ if ext in extensions:
+ out.append(f)
+ else:
+ out.append(file)
+ return out
+
+
+def make_diff(file, original, reformatted):
+ return list(
+ difflib.unified_diff(
+ original,
+ reformatted,
+ fromfile='{}\t(original)'.format(file),
+ tofile='{}\t(reformatted)'.format(file),
+ n=3))
+
+
+class DiffError(Exception):
+ def __init__(self, message, errs=None):
+ super(DiffError, self).__init__(message)
+ self.errs = errs or []
+
+
+class UnexpectedError(Exception):
+ def __init__(self, message, exc=None):
+ super(UnexpectedError, self).__init__(message)
+ self.formatted_traceback = traceback.format_exc()
+ self.exc = exc
+
+
+def run_clang_format_diff_wrapper(args, file):
+ try:
+ ret = run_clang_format_diff(args, file)
+ return ret
+ except DiffError:
+ raise
+ except Exception as e:
+ raise UnexpectedError('{}: {}: {}'.format(file, e.__class__.__name__,
+ e), e)
+
+
+def run_clang_format_diff(args, file):
+ try:
+ with io.open(file, 'r', encoding='utf-8') as f:
+ original = f.readlines()
+ except IOError as exc:
+ raise DiffError(str(exc))
+ invocation = [args.clang_format_executable, file]
+
+ # Use of utf-8 to decode the process output.
+ #
+ # Hopefully, this is the correct thing to do.
+ #
+ # It's done due to the following assumptions (which may be incorrect):
+ # - clang-format will returns the bytes read from the files as-is,
+ # without conversion, and it is already assumed that the files use utf-8.
+ # - if the diagnostics were internationalized, they would use utf-8:
+ # > Adding Translations to Clang
+ # >
+ # > Not possible yet!
+ # > Diagnostic strings should be written in UTF-8,
+ # > the client can translate to the relevant code page if needed.
+ # > Each translation completely replaces the format string
+ # > for the diagnostic.
+ # > -- http://clang.llvm.org/docs/InternalsManual.html#internals-diag-translation
+ #
+ # It's not pretty, due to Python 2 & 3 compatibility.
+ encoding_py3 = {}
+ if sys.version_info[0] >= 3:
+ encoding_py3['encoding'] = 'utf-8'
+
+ try:
+ proc = subprocess.Popen(
+ invocation,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ **encoding_py3)
+ except OSError as exc:
+ raise DiffError(
+ "Command '{}' failed to start: {}".format(
+ subprocess.list2cmdline(invocation), exc
+ )
+ )
+ proc_stdout = proc.stdout
+ proc_stderr = proc.stderr
+ if sys.version_info[0] < 3:
+ # make the pipes compatible with Python 3,
+ # reading lines should output unicode
+ encoding = 'utf-8'
+ proc_stdout = codecs.getreader(encoding)(proc_stdout)
+ proc_stderr = codecs.getreader(encoding)(proc_stderr)
+ # hopefully the stderr pipe won't get full and block the process
+ outs = list(proc_stdout.readlines())
+ errs = list(proc_stderr.readlines())
+ proc.wait()
+ if proc.returncode:
+ raise DiffError(
+ "Command '{}' returned non-zero exit status {}".format(
+ subprocess.list2cmdline(invocation), proc.returncode
+ ),
+ errs,
+ )
+ return make_diff(file, original, outs), errs
+
+
+def bold_red(s):
+ return '\x1b[1m\x1b[31m' + s + '\x1b[0m'
+
+
+def colorize(diff_lines):
+ def bold(s):
+ return '\x1b[1m' + s + '\x1b[0m'
+
+ def cyan(s):
+ return '\x1b[36m' + s + '\x1b[0m'
+
+ def green(s):
+ return '\x1b[32m' + s + '\x1b[0m'
+
+ def red(s):
+ return '\x1b[31m' + s + '\x1b[0m'
+
+ for line in diff_lines:
+ if line[:4] in ['--- ', '+++ ']:
+ yield bold(line)
+ elif line.startswith('@@ '):
+ yield cyan(line)
+ elif line.startswith('+'):
+ yield green(line)
+ elif line.startswith('-'):
+ yield red(line)
+ else:
+ yield line
+
+
+def print_diff(diff_lines, use_color):
+ if use_color:
+ diff_lines = colorize(diff_lines)
+ if sys.version_info[0] < 3:
+ sys.stdout.writelines((l.encode('utf-8') for l in diff_lines))
+ else:
+ sys.stdout.writelines(diff_lines)
+
+
+def print_trouble(prog, message, use_colors):
+ error_text = 'error:'
+ if use_colors:
+ error_text = bold_red(error_text)
+ print("{}: {} {}".format(prog, error_text, message), file=sys.stderr)
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument(
+ '--clang-format-executable',
+ metavar='EXECUTABLE',
+ help='path to the clang-format executable',
+ default='clang-format')
+ parser.add_argument(
+ '--extensions',
+ help='comma separated list of file extensions (default: {})'.format(
+ DEFAULT_EXTENSIONS),
+ default=DEFAULT_EXTENSIONS)
+ parser.add_argument(
+ '-r',
+ '--recursive',
+ action='store_true',
+ help='run recursively over directories')
+ parser.add_argument('files', metavar='file', nargs='+')
+ parser.add_argument(
+ '-q',
+ '--quiet',
+ action='store_true')
+ parser.add_argument(
+ '-j',
+ metavar='N',
+ type=int,
+ default=0,
+ help='run N clang-format jobs in parallel'
+ ' (default number of cpus + 1)')
+ parser.add_argument(
+ '--color',
+ default='auto',
+ choices=['auto', 'always', 'never'],
+ help='show colored diff (default: auto)')
+ parser.add_argument(
+ '-e',
+ '--exclude',
+ metavar='PATTERN',
+ action='append',
+ default=[],
+ help='exclude paths matching the given glob-like pattern(s)'
+ ' from recursive search')
+
+ args = parser.parse_args()
+
+ # use default signal handling, like diff return SIGINT value on ^C
+ # https://bugs.python.org/issue14229#msg156446
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ try:
+ signal.SIGPIPE
+ except AttributeError:
+ # compatibility, SIGPIPE does not exist on Windows
+ pass
+ else:
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
+
+ colored_stdout = False
+ colored_stderr = False
+ if args.color == 'always':
+ colored_stdout = True
+ colored_stderr = True
+ elif args.color == 'auto':
+ colored_stdout = sys.stdout.isatty()
+ colored_stderr = sys.stderr.isatty()
+
+ version_invocation = [args.clang_format_executable, str("--version")]
+ try:
+ subprocess.check_call(version_invocation, stdout=DEVNULL)
+ except subprocess.CalledProcessError as e:
+ print_trouble(parser.prog, str(e), use_colors=colored_stderr)
+ return ExitStatus.TROUBLE
+ except OSError as e:
+ print_trouble(
+ parser.prog,
+ "Command '{}' failed to start: {}".format(
+ subprocess.list2cmdline(version_invocation), e
+ ),
+ use_colors=colored_stderr,
+ )
+ return ExitStatus.TROUBLE
+
+ retcode = ExitStatus.SUCCESS
+ files = list_files(
+ args.files,
+ recursive=args.recursive,
+ exclude=args.exclude,
+ extensions=args.extensions.split(','))
+
+ if not files:
+ return
+
+ njobs = args.j
+ if njobs == 0:
+ njobs = multiprocessing.cpu_count() + 1
+ njobs = min(len(files), njobs)
+
+ if njobs == 1:
+ # execute directly instead of in a pool,
+ # less overhead, simpler stacktraces
+ it = (run_clang_format_diff_wrapper(args, file) for file in files)
+ pool = None
+ else:
+ pool = multiprocessing.Pool(njobs)
+ it = pool.imap_unordered(
+ partial(run_clang_format_diff_wrapper, args), files)
+ while True:
+ try:
+ outs, errs = next(it)
+ except StopIteration:
+ break
+ except DiffError as e:
+ print_trouble(parser.prog, str(e), use_colors=colored_stderr)
+ retcode = ExitStatus.TROUBLE
+ sys.stderr.writelines(e.errs)
+ except UnexpectedError as e:
+ print_trouble(parser.prog, str(e), use_colors=colored_stderr)
+ sys.stderr.write(e.formatted_traceback)
+ retcode = ExitStatus.TROUBLE
+ # stop at the first unexpected error,
+ # something could be very wrong,
+ # don't process all files unnecessarily
+ if pool:
+ pool.terminate()
+ break
+ else:
+ sys.stderr.writelines(errs)
+ if outs == []:
+ continue
+ if not args.quiet:
+ print_diff(outs, use_color=colored_stdout)
+ if retcode == ExitStatus.SUCCESS:
+ retcode = ExitStatus.DIFF
+ return retcode
+
+
+if __name__ == '__main__':
+ sys.exit(main())
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+python $DIR/run-clang-format.py -r $DIR/../src/**/ $DIR/../include/**/
\ No newline at end of file
endif()
project(JSONCPP
- VERSION 1.9.0 # <major>[.<minor>[.<patch>[.<tweak>]]]
+ # Note: version must be updated in three places when doing a release. This
+ # annoying process ensures that amalgamate, CMake, and meson all report the
+ # correct version.
+ # 1. /meson.build
+ # 2. /include/json/version.h
+ # 3. /CMakeLists.txt
+ # IMPORTANT: also update the SOVERSION!!
+ VERSION 1.9.2 # <major>[.<minor>[.<patch>[.<tweak>]]]
LANGUAGES CXX)
message(STATUS "JsonCpp Version: ${JSONCPP_VERSION_MAJOR}.${JSONCPP_VERSION_MINOR}.${JSONCPP_VERSION_PATCH}")
-set( JSONCPP_SOVERSION 21 )
+set( JSONCPP_SOVERSION 22 )
option(JSONCPP_WITH_TESTS "Compile and (for jsoncpp_check) run JsonCpp test executables" ON)
option(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" ON)
set(JSONCPP_USE_SECURE_MEMORY "0" CACHE STRING "-D...=1 to use memory-wiping allocator for STL" )
-# File version.h is only regenerated on CMake configure step
-configure_file( "${PROJECT_SOURCE_DIR}/src/lib_json/version.h.in"
- "${PROJECT_BINARY_DIR}/include/json/version.h"
- NEWLINE_STYLE UNIX )
configure_file( "${PROJECT_SOURCE_DIR}/version.in"
"${PROJECT_BINARY_DIR}/version"
NEWLINE_STYLE UNIX )
if(MSVC)
# Only enabled in debug because some old versions of VS STL generate
# warnings when compiled in release configuration.
- add_compile_options($<$<CONFIG:Debug>:/WX>)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
+ add_compile_options($<$<CONFIG:Debug>:/WX>)
+ else()
+ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /WX ")
+ endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
- add_compile_options(-Werror)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
+ add_compile_options(-Werror)
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
+ endif()
if(JSONCPP_WITH_STRICT_ISO)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
add_compile_options(-pedantic-errors)
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic-errors")
+ endif()
endif()
endif()
endmacro()
if(MSVC)
# Only enabled in debug because some old versions of VS STL generate
# unreachable code warning when compiled in release configuration.
- add_compile_options($<$<CONFIG:Debug>:/W4>)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
+ add_compile_options($<$<CONFIG:Debug>:/W4>)
+ else()
+ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /W4 ")
+ endif()
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# using regular Clang or AppleClang
- add_compile_options(-Wall -Wconversion -Wshadow -Werror=conversion -Werror=sign-compare)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
+ add_compile_options(-Wall -Wconversion -Wshadow -Werror=conversion -Werror=sign-compare)
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wconversion -Wshadow -Werror=conversion -Werror=sign-compare")
+ endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# using GCC
- add_compile_options(-Wall -Wconversion -Wshadow -Wextra)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
+ add_compile_options(-Wall -Wconversion -Wshadow -Wextra)
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wconversion -Wshadow -Wextra")
+ endif()
# not yet ready for -Wsign-conversion
if(JSONCPP_WITH_STRICT_ISO)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
add_compile_options(-pedantic)
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
+ endif()
endif()
if(JSONCPP_WITH_WARNING_AS_ERROR)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
add_compile_options(-Werror=conversion)
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=conversion")
+ endif()
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
# using Intel compiler
- add_compile_options(-Wall -Wconversion -Wshadow -Wextra -Werror=conversion)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
+ add_compile_options(-Wall -Wconversion -Wshadow -Wextra -Werror=conversion)
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wconversion -Wshadow -Wextra -Werror=conversion")
+ endif()
if(JSONCPP_WITH_STRICT_ISO AND NOT JSONCPP_WITH_WARNING_AS_ERROR)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
add_compile_options(-pedantic)
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
+ endif()
endif()
endif()
#install the includes
add_subdirectory( include )
+#install the example
+add_subdirectory( example )
Thanks to David Seifert (@SoapGentoo), we (the maintainers) now use
[meson](http://mesonbuild.com/) and [ninja](https://ninja-build.org/) to build
for debugging, as well as for continuous integration (see
-[`./travis_scripts/meson_builder.sh`](./travis_scripts/meson_builder.sh) ). Other systems may work, but minor
+[`./.travis_scripts/meson_builder.sh`](./.travis_scripts/meson_builder.sh) ). Other systems may work, but minor
things like version strings might break.
First, install both meson (which requires Python3) and ninja.
LIB_TYPE=shared
#LIB_TYPE=static
meson --buildtype ${BUILD_TYPE} --default-library ${LIB_TYPE} . build-${LIB_TYPE}
- #ninja -v -C build-${LIB_TYPE} test # This stopped working on my Mac.
ninja -v -C build-${LIB_TYPE}
cd build-${LIB_TYPE}
meson test --no-rebuild --print-errorlogs
# JsonCpp
[![badge](https://img.shields.io/badge/conan.io-jsoncpp%2F1.8.0-green.svg?logo=data:image/png;base64%2CiVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAA1VBMVEUAAABhlctjlstkl8tlmMtlmMxlmcxmmcxnmsxpnMxpnM1qnc1sn85voM91oM11oc1xotB2oc56pNF6pNJ2ptJ8ptJ8ptN9ptN8p9N5qNJ9p9N9p9R8qtOBqdSAqtOAqtR%2BrNSCrNJ/rdWDrNWCsNWCsNaJs9eLs9iRvNuVvdyVv9yXwd2Zwt6axN6dxt%2Bfx%2BChyeGiyuGjyuCjyuGly%2BGlzOKmzOGozuKoz%2BKqz%2BOq0OOv1OWw1OWw1eWx1eWy1uay1%2Baz1%2Baz1%2Bez2Oe02Oe12ee22ujUGwH3AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgBQkREyOxFIh/AAAAiklEQVQI12NgAAMbOwY4sLZ2NtQ1coVKWNvoc/Eq8XDr2wB5Ig62ekza9vaOqpK2TpoMzOxaFtwqZua2Bm4makIM7OzMAjoaCqYuxooSUqJALjs7o4yVpbowvzSUy87KqSwmxQfnsrPISyFzWeWAXCkpMaBVIC4bmCsOdgiUKwh3JojLgAQ4ZCE0AMm2D29tZwe6AAAAAElFTkSuQmCC)](https://bintray.com/theirix/conan-repo/jsoncpp%3Atheirix)
+[![badge](https://img.shields.io/badge/license-MIT-blue)](https://github.com/open-source-parsers/jsoncpp/blob/master/LICENSE)
+[![badge](https://img.shields.io/badge/document-doxygen-brightgreen)](http://open-source-parsers.github.io/jsoncpp-docs/doxygen/index.html)
+[![Coverage Status](https://coveralls.io/repos/github/open-source-parsers/jsoncpp/badge.svg?branch=master)](https://coveralls.io/github/open-source-parsers/jsoncpp?branch=master)
+
[JSON][json-org] is a lightweight data-interchange format. It can represent
numbers, strings, ordered sequences of values, and collections of name/value
## Using JsonCpp in your project
+### The vcpkg dependency manager
+You can download and install JsonCpp using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
+
+ git clone https://github.com/Microsoft/vcpkg.git
+ cd vcpkg
+ ./bootstrap-vcpkg.sh
+ ./vcpkg integrate install
+ vcpkg install jsoncpp
+
+The JsonCpp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
+
### Amalgamated source
-https://github.com/open-source-parsers/jsoncpp/wiki/Amalgamated
+https://github.com/open-source-parsers/jsoncpp/wiki/Amalgamated-(Possibly-outdated)
### The Meson Build System
If you are using the [Meson Build System](http://mesonbuild.com), then you can get a wrap file by downloading it from [Meson WrapDB](https://wrapdb.mesonbuild.com/jsoncpp), or simply use `meson wrap install jsoncpp`.
### Other ways
-If you have trouble, see the Wiki, or post a question as an Issue.
+If you have trouble, see the [Wiki](https://github.com/open-source-parsers/jsoncpp/wiki), or post a question as an Issue.
## License
+#!/usr/bin/env python
+
"""Amalgamate json-cpp library sources into a single source and header file.
Works with python2.6+ and python3.4+.
import os.path
import sys
+INCLUDE_PATH = "include/json"
+SRC_PATH = "src/lib_json"
+
class AmalgamationFile:
def __init__(self, top_dir):
self.top_dir = top_dir
header.add_text("/// If defined, indicates that the source file is amalgamated")
header.add_text("/// to prevent private header inclusion.")
header.add_text("#define JSON_IS_AMALGAMATION")
- header.add_file("include/json/version.h")
- header.add_file("include/json/allocator.h")
- header.add_file("include/json/config.h")
- header.add_file("include/json/forwards.h")
- header.add_file("include/json/features.h")
- header.add_file("include/json/value.h")
- header.add_file("include/json/reader.h")
- header.add_file("include/json/writer.h")
- header.add_file("include/json/assertions.h")
+ header.add_file(os.path.join(INCLUDE_PATH, "version.h"))
+ header.add_file(os.path.join(INCLUDE_PATH, "allocator.h"))
+ header.add_file(os.path.join(INCLUDE_PATH, "config.h"))
+ header.add_file(os.path.join(INCLUDE_PATH, "forwards.h"))
+ header.add_file(os.path.join(INCLUDE_PATH, "json_features.h"))
+ header.add_file(os.path.join(INCLUDE_PATH, "value.h"))
+ header.add_file(os.path.join(INCLUDE_PATH, "reader.h"))
+ header.add_file(os.path.join(INCLUDE_PATH, "writer.h"))
+ header.add_file(os.path.join(INCLUDE_PATH, "assertions.h"))
header.add_text("#endif //ifndef JSON_AMALGAMATED_H_INCLUDED")
target_header_path = os.path.join(os.path.dirname(target_source_path), header_include_path)
header.add_text("/// If defined, indicates that the source file is amalgamated")
header.add_text("/// to prevent private header inclusion.")
header.add_text("#define JSON_IS_AMALGAMATION")
- header.add_file("include/json/config.h")
- header.add_file("include/json/forwards.h")
+ header.add_file(os.path.join(INCLUDE_PATH, "config.h"))
+ header.add_file(os.path.join(INCLUDE_PATH, "forwards.h"))
header.add_text("#endif //ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED")
target_forward_header_path = os.path.join(os.path.dirname(target_source_path),
#endif
""")
source.add_text("")
- lib_json = "src/lib_json"
- source.add_file(os.path.join(lib_json, "json_tool.h"))
- source.add_file(os.path.join(lib_json, "json_reader.cpp"))
- source.add_file(os.path.join(lib_json, "json_valueiterator.inl"))
- source.add_file(os.path.join(lib_json, "json_value.cpp"))
- source.add_file(os.path.join(lib_json, "json_writer.cpp"))
+ source.add_file(os.path.join(SRC_PATH, "json_tool.h"))
+ source.add_file(os.path.join(SRC_PATH, "json_reader.cpp"))
+ source.add_file(os.path.join(SRC_PATH, "json_valueiterator.inl"))
+ source.add_file(os.path.join(SRC_PATH, "json_value.cpp"))
+ source.add_file(os.path.join(SRC_PATH, "json_writer.cpp"))
print("Writing amalgamated source to %r" % target_source_path)
source.write_to(target_source_path)
\section _intro Introduction
<a HREF="http://www.json.org/">JSON (JavaScript Object Notation)</a>
- is a lightweight data-interchange format.
+ is a lightweight data-interchange format.
Here is an example of JSON data:
\verbatim
{
// Default encoding for text
"encoding" : "UTF-8",
-
+
// Plug-ins loaded at start-up
"plug-ins" : [
"python",
"c++", // trailing comment
"ruby"
],
-
+
// Tab indent size
// (multi-line comment)
"indent" : { /*embedded comment*/ "length" : 3, "use_space": true }
// 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() );
\endcode
\section _pbuild Build instructions
-The build instructions are located in the file
+The build instructions are located in the file
<a HREF="https://github.com/open-source-parsers/jsoncpp/blob/master/README.md">README.md</a> in the top-directory of the project.
The latest version of the source is available in the project's GitHub repository:
jsoncpp</a>
\section _news What's New?
-The description of latest changes can be found in
+The description of latest changes can be found in
<a HREF="https://github.com/open-source-parsers/jsoncpp/wiki/NEWS">
the NEWS wiki
</a>.
\section _license License
See file <a href="https://github.com/open-source-parsers/jsoncpp/blob/master/LICENSE"><code>LICENSE</code></a> in the top-directory of the project.
-Basically JsonCpp is licensed under MIT license, or public domain if desired
+Basically JsonCpp is licensed under MIT license, or public domain if desired
and recognized in your jurisdiction.
\author Baptiste Lepilleur <blep@users.sourceforge.net> (originator)
--- /dev/null
+#vim: et ts =4 sts = 4 sw = 4 tw = 0
+cmake_minimum_required(VERSION 3.1)
+
+set(EXAMPLES
+ readFromString
+ readFromStream
+ stringWrite
+ streamWrite
+ )
+add_definitions(-D_GLIBCXX_USE_CXX11_ABI)
+set_property(DIRECTORY PROPERTY COMPILE_OPTIONS ${EXTRA_CXX_FLAGS})
+
+if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra ")
+else()
+ add_definitions(
+ -D_SCL_SECURE_NO_WARNINGS
+ -D_CRT_SECURE_NO_WARNINGS
+ -D_WIN32_WINNT=0x601
+ -D_WINSOCK_DEPRECATED_NO_WARNINGS)
+endif()
+
+foreach (example ${EXAMPLES})
+ add_executable(${example} ${example}/${example}.cpp)
+ target_include_directories(${example} PUBLIC ${CMAKE_SOURCE_DIR}/include)
+ target_link_libraries(${example} jsoncpp_lib)
+endforeach()
+
+add_custom_target(examples ALL DEPENDS ${EXAMPLES})
--- /dev/null
+***NOTE***
+
+If you get linker errors about undefined references to symbols that involve types in the `std::__cxx11` namespace or the tag
+`[abi:cxx11]` then it probably indicates that you are trying to link together object files that were compiled with different
+values for the _GLIBCXX_USE_CXX11_ABI marco. This commonly happens when linking to a third-party library that was compiled with
+an older version of GCC. If the third-party library cannot be rebuilt with the new ABI, then you need to recompile your code with
+the old ABI,just like:
+**g++ stringWrite.cpp -ljsoncpp -std=c++11 -D_GLIBCXX_USE_CXX11_ABI=0 -o stringWrite**
+
+Not all of uses of the new ABI will cause changes in symbol names, for example a class with a `std::string` member variable will
+have the same mangled name whether compiled with the older or new ABI. In order to detect such problems, the new types and functions
+are annotated with the abi_tag attribute, allowing the compiler to warn about potential ABI incompatibilities in code using them.
+Those warnings can be enabled with the `-Wabi-tag` option.
--- /dev/null
+{
+ 1: "value"
+}
\ No newline at end of file
--- /dev/null
+#include "json/json.h"
+#include <fstream>
+#include <iostream>
+/** \brief Parse from stream, collect comments and capture error info.
+ * Example Usage:
+ * $g++ readFromStream.cpp -ljsoncpp -std=c++11 -o readFromStream
+ * $./readFromStream
+ * // comment head
+ * {
+ * // comment before
+ * "key" : "value"
+ * }
+ * // comment after
+ * // comment tail
+ */
+int main(int argc, char* argv[]) {
+ Json::Value root;
+ std::ifstream ifs;
+ ifs.open(argv[1]);
+
+ Json::CharReaderBuilder builder;
+ builder["collectComments"] = true;
+ JSONCPP_STRING errs;
+ if (!parseFromStream(builder, ifs, &root, &errs)) {
+ std::cout << errs << std::endl;
+ return EXIT_FAILURE;
+ }
+ std::cout << root << std::endl;
+ return EXIT_SUCCESS;
+}
--- /dev/null
+// comment head
+{
+ // comment before
+ "key" : "value"
+ // comment after
+}// comment tail
--- /dev/null
+#include "json/json.h"
+#include <iostream>
+/**
+ * \brief Parse a raw string into Value object using the CharReaderBuilder
+ * class, or the legacy Reader class.
+ * Example Usage:
+ * $g++ readFromString.cpp -ljsoncpp -std=c++11 -o readFromString
+ * $./readFromString
+ * colin
+ * 20
+ */
+int main() {
+ const std::string rawJson = R"({"Age": 20, "Name": "colin"})";
+ const int rawJsonLength = static_cast<int>(rawJson.length());
+ constexpr bool shouldUseOldWay = false;
+ JSONCPP_STRING err;
+ Json::Value root;
+
+ if (shouldUseOldWay) {
+ Json::Reader reader;
+ reader.parse(rawJson, root);
+ } else {
+ Json::CharReaderBuilder builder;
+ const std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
+ if (!reader->parse(rawJson.c_str(), rawJson.c_str() + rawJsonLength, &root,
+ &err)) {
+ std::cout << "error" << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+ const std::string name = root["Name"].asString();
+ const int age = root["Age"].asInt();
+
+ std::cout << name << std::endl;
+ std::cout << age << std::endl;
+ return EXIT_SUCCESS;
+}
--- /dev/null
+#include "json/json.h"
+#include <iostream>
+/** \brief Write the Value object to a stream.
+ * Example Usage:
+ * $g++ streamWrite.cpp -ljsoncpp -std=c++11 -o streamWrite
+ * $./streamWrite
+ * {
+ * "Age" : 20,
+ * "Name" : "robin"
+ * }
+ */
+int main() {
+ Json::Value root;
+ Json::StreamWriterBuilder builder;
+ const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
+
+ root["Name"] = "robin";
+ root["Age"] = 20;
+ writer->write(root, &std::cout);
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+#include "json/json.h"
+#include <iostream>
+/** \brief Write a Value object to a string.
+ * Example Usage:
+ * $g++ stringWrite.cpp -ljsoncpp -std=c++11 -o stringWrite
+ * $./stringWrite
+ * {
+ * "action" : "run",
+ * "data" :
+ * {
+ * "number" : 1
+ * }
+ * }
+ */
+int main() {
+ Json::Value root;
+ Json::Value data;
+ constexpr bool shouldUseOldWay = false;
+ root["action"] = "run";
+ data["number"] = 1;
+ root["data"] = data;
+
+ if (shouldUseOldWay) {
+ Json::FastWriter writer;
+ const std::string json_file = writer.write(root);
+ std::cout << json_file << std::endl;
+ } else {
+ Json::StreamWriterBuilder builder;
+ const std::string json_file = Json::writeString(builder, root);
+ std::cout << json_file << std::endl;
+ }
+ return EXIT_SUCCESS;
+}
file(GLOB INCLUDE_FILES "json/*.h")
install(FILES
${INCLUDE_FILES}
- ${PROJECT_BINARY_DIR}/include/json/version.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/json)
#if defined(_MSC_VER) && _MSC_VER < 1900
// As recommended at
// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
-extern JSON_API int
-msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...);
+extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
+ const char* format, ...);
#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
#else
#define jsoncpp_snprintf std::snprintf
#if __has_extension(attribute_deprecated_with_message)
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#endif
-#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
+#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc)
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
-#if __GNUC__ >= 6
+#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
#endif
#endif // if defined(JSON_NO_INT64)
template <typename T>
-using Allocator = typename std::conditional<JSONCPP_USING_SECURE_MEMORY,
- SecureAllocator<T>,
- std::allocator<T>>::type;
+using Allocator =
+ typename std::conditional<JSONCPP_USING_SECURE_MEMORY, SecureAllocator<T>,
+ std::allocator<T>>::type;
using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
-using IStringStream = std::basic_istringstream<String::value_type,
- String::traits_type,
- String::allocator_type>;
-using OStringStream = std::basic_ostringstream<String::value_type,
- String::traits_type,
- String::allocator_type>;
+using IStringStream =
+ std::basic_istringstream<String::value_type, String::traits_type,
+ String::allocator_type>;
+using OStringStream =
+ std::basic_ostringstream<String::value_type, String::traits_type,
+ String::allocator_type>;
using IStream = std::istream;
using OStream = std::ostream;
} // namespace Json
class CharReader;
class CharReaderBuilder;
-// features.h
+// json_features.h
class Features;
// value.h
#define JSON_JSON_H_INCLUDED
#include "autolink.h"
-#include "features.h"
+#include "json_features.h"
#include "reader.h"
#include "value.h"
#include "writer.h"
#define CPPTL_JSON_READER_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
-#include "features.h"
+#include "json_features.h"
#include "value.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <deque>
namespace Json {
/** \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 JSONCPP_DEPRECATED(
+ "Use CharReader and CharReaderBuilder instead.") JSON_API Reader {
public:
typedef char Char;
typedef const Char* Location;
*
* The offsets give the [start, limit) range of bytes within the text. Note
* that this is bytes, not codepoints.
- *
*/
struct StructuredError {
ptrdiff_t offset_start;
String message;
};
- /** \brief Constructs a Reader allowing all features
- * for parsing.
+ /** \brief Constructs a Reader allowing all features for parsing.
*/
JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
Reader();
- /** \brief Constructs a Reader allowing the specified feature set
- * for parsing.
+ /** \brief Constructs a Reader allowing the specified feature set for parsing.
*/
JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
Reader(const Features& features);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document.
- * \param document UTF-8 encoded string containing the document to read.
- * \param root [out] Contains the root value of the document if it was
- * successfully parsed.
- * \param collectComments \c true to collect comment and allow writing them
- * back during
- * serialization, \c false to discard comments.
- * This parameter is ignored if
- * Features::allowComments_
- * is \c false.
+ *
+ * \param document UTF-8 encoded string containing the document
+ * to read.
+ * \param[out] root Contains the root value of the document if it
+ * was successfully parsed.
+ * \param collectComments \c true to collect comment and allow writing
+ * them back during serialization, \c false to
+ * discard comments. This parameter is ignored
+ * if Features::allowComments_ is \c false.
* \return \c true if the document was successfully parsed, \c false if an
* error occurred.
*/
- bool
- parse(const std::string& document, Value& root, bool collectComments = true);
+ bool parse(const std::string& document, Value& root,
+ bool collectComments = true);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
- document.
- * \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 collectComments \c true to collect comment and allow writing them
- back during
- * serialization, \c false to discard comments.
- * This parameter is ignored if
- Features::allowComments_
- * is \c false.
+ * document.
+ *
+ * \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[out] root Contains the root value of the document if it
+ * was successfully parsed.
+ * \param collectComments \c true to collect comment and allow writing
+ * them back during serialization, \c false to
+ * discard comments. This parameter is ignored
+ * if Features::allowComments_ is \c false.
* \return \c true if the document was successfully parsed, \c false if an
- error occurred.
+ * error occurred.
*/
- bool parse(const char* beginDoc,
- const char* endDoc,
- Value& root,
+ bool parse(const char* beginDoc, const char* endDoc, Value& root,
bool collectComments = true);
/// \brief Parse from input stream.
/** \brief Returns a user friendly string that list errors in the parsed
* document.
- * \return Formatted error message with the list of errors with their location
- * in
- * the parsed document. An empty string is returned if no error
- * occurred
- * during parsing.
+ *
+ * \return Formatted error message with the list of errors with their
+ * location in the parsed document. An empty string is returned if no error
+ * occurred during parsing.
* \deprecated Use getFormattedErrorMessages() instead (typo fix).
*/
JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.")
/** \brief Returns a user friendly string that list errors in the parsed
* document.
- * \return Formatted error message with the list of errors with their location
- * in
- * the parsed document. An empty string is returned if no error
- * occurred
- * during parsing.
+ *
+ * \return Formatted error message with the list of errors with their
+ * location in the parsed document. An empty string is returned if no error
+ * occurred during parsing.
*/
String getFormattedErrorMessages() const;
- /** \brief Returns a vector of structured erros encounted while parsing.
+ /** \brief Returns a vector of structured errors encountered while parsing.
+ *
* \return A (possibly empty) vector of StructuredError objects. Currently
- * only one error can be returned, but the caller should tolerate
- * multiple
- * errors. This can occur if the parser recovers from a non-fatal
- * parse error and then encounters additional errors.
+ * only one error can be returned, but the caller should tolerate multiple
+ * errors. This can occur if the parser recovers from a non-fatal parse
+ * error and then encounters additional errors.
*/
std::vector<StructuredError> getStructuredErrors() const;
/** \brief Add a semantic error message.
- * \param value JSON Value location associated with the error
+ *
+ * \param value JSON Value location associated with the error
* \param message The error message.
- * \return \c true if the error was successfully added, \c false if the
- * Value offset exceeds the document size.
+ * \return \c true if the error was successfully added, \c false if the Value
+ * offset exceeds the document size.
*/
bool pushError(const Value& value, const String& message);
/** \brief Add a semantic error message with extra context.
- * \param value JSON Value location associated with the error
+ *
+ * \param value JSON Value location associated with the error
* \param message The error message.
- * \param extra Additional JSON Value location to contextualize the error
+ * \param extra Additional JSON Value location to contextualize the error
* \return \c true if the error was successfully added, \c false if either
* Value offset exceeds the document size.
*/
bool pushError(const Value& value, const String& message, const Value& extra);
/** \brief Return whether there are any errors.
- * \return \c true if there are no errors to report \c false if
- * errors have occurred.
+ *
+ * \return \c true if there are no errors to report \c false if errors have
+ * occurred.
*/
bool good() const;
bool readToken(Token& token);
void skipSpaces();
- bool match(Location pattern, int patternLength);
+ bool match(const Char* pattern, int patternLength);
bool readComment();
bool readCStyleComment();
bool readCppStyleComment();
bool decodeString(Token& token, String& decoded);
bool decodeDouble(Token& token);
bool decodeDouble(Token& token, Value& decoded);
- bool decodeUnicodeCodePoint(Token& token,
- Location& current,
- Location end,
+ bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
unsigned int& unicode);
- bool decodeUnicodeEscapeSequence(Token& token,
- Location& current,
- Location end,
- unsigned int& unicode);
+ bool decodeUnicodeEscapeSequence(Token& token, Location& current,
+ Location end, unsigned int& unicode);
bool addError(const String& message, Token& token, Location extra = nullptr);
bool recoverFromError(TokenType skipUntilToken);
- bool addErrorAndRecover(const String& message,
- Token& token,
+ bool addErrorAndRecover(const String& message, Token& token,
TokenType skipUntilToken);
void skipUntilSpace();
Value& currentValue();
Char getNextChar();
- void
- getLocationLineAndColumn(Location location, int& line, int& column) const;
+ void getLocationLineAndColumn(Location location, int& line,
+ int& column) const;
String getLocationLineAndColumn(Location location) const;
void addComment(Location begin, Location end, CommentPlacement placement);
void skipCommentTokens(Token& token);
public:
virtual ~CharReader() = default;
/** \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.
+ * 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.
+ * \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[out] root Contains the root value of the document if it was
+ * successfully parsed.
+ * \param[out] errs 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.
+ * error occurred.
*/
- virtual bool parse(char const* beginDoc,
- char const* endDoc,
- Value* root,
+ virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) = 0;
class JSON_API Factory {
}; // CharReader
/** \brief Build a CharReader implementation.
-
-Usage:
-\code
- using namespace Json;
- CharReaderBuilder builder;
- builder["collectComments"] = false;
- Value value;
- String errs;
- bool ok = parseFromStream(builder, std::cin, &value, &errs);
-\endcode
-*/
+ *
+ * Usage:
+ * \code
+ * using namespace Json;
+ * CharReaderBuilder builder;
+ * builder["collectComments"] = false;
+ * Value value;
+ * 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.
- - `"allowSingleQuotes": false or true`
- - true if '' are allowed for strings (both keys and values)
- - `"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.
- - `"rejectDupKeys": false or true`
- - If true, `parse()` returns false when a key is duplicated within an
- object.
- - `"allowSpecialFloats": false or true`
- - If true, special float values (NaNs and infinities) are allowed
- and their values are lossfree restorable.
-
- You can examine 'settings_` yourself
- to see the defaults. You can also write and read them just like any
- JSON Value.
- \sa setDefaults()
- */
+ * 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.
+ * - `"allowSingleQuotes": false or true`
+ * - true if '' are allowed for strings (both keys and values)
+ * - `"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.
+ * - `"rejectDupKeys": false or true`
+ * - If true, `parse()` returns false when a key is duplicated within an
+ * object.
+ * - `"allowSpecialFloats": false or true`
+ * - If true, special float values (NaNs and infinities) are allowed and
+ * their values are lossfree restorable.
+ *
+ * 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();
* Someday we might have a real StreamReader, but for now this
* is convenient.
*/
-bool JSON_API parseFromStream(CharReader::Factory const&,
- IStream&,
- Value* root,
+bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root,
String* errs);
/** \brief Read from 'sin' into 'root'.
-
- Always keep comments from the input JSON.
-
- This can be used to read a file into a particular sub-object.
- For example:
- \code
- Json::Value root;
- cin >> root["dir"]["file"];
- cout << root;
- \endcode
- Result:
- \verbatim
- {
- "dir": {
- "file": {
- // The input stream JSON would be nested here.
- }
- }
- }
- \endverbatim
- \throw std::exception on parse error.
- \see Json::operator<<()
-*/
+ *
+ * Always keep comments from the input JSON.
+ *
+ * This can be used to read a file into a particular sub-object.
+ * For example:
+ * \code
+ * Json::Value root;
+ * cin >> root["dir"]["file"];
+ * cout << root;
+ * \endcode
+ * Result:
+ * \verbatim
+ * {
+ * "dir": {
+ * "file": {
+ * // The input stream JSON would be nested here.
+ * }
+ * }
+ * }
+ * \endverbatim
+ * \throw std::exception on parse error.
+ * \see Json::operator<<()
+ */
JSON_API IStream& operator>>(IStream&, Value&);
} // namespace Json
#if !defined(JSON_IS_AMALGAMATION)
#include "forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
+
+// Conditional NORETURN attribute on the throw functions would:
+// a) suppress false positives from static code analysis
+// b) possibly improve optimization opportunities.
+#if !defined(JSONCPP_NORETURN)
+#if defined(_MSC_VER) && _MSC_VER == 1800
+#define JSONCPP_NORETURN __declspec(noreturn)
+#else
+#define JSONCPP_NORETURN [[noreturn]]
+#endif
+#endif
+
#include <array>
#include <exception>
#include <memory>
#endif
/// used internally
-[[noreturn]] void throwRuntimeError(String const& msg);
+JSONCPP_NORETURN void throwRuntimeError(String const& msg);
/// used internally
-[[noreturn]] void throwLogicError(String const& msg);
+JSONCPP_NORETURN void throwLogicError(String const& msg);
/** \brief Type of the value held by a Value object.
*/
static Value const& nullSingleton();
/// Minimum signed integer value that can be stored in a Json::Value.
- static const LargestInt minLargestInt;
+ static constexpr LargestInt minLargestInt =
+ LargestInt(~(LargestUInt(-1) / 2));
/// Maximum signed integer value that can be stored in a Json::Value.
- static const LargestInt maxLargestInt;
+ static constexpr LargestInt maxLargestInt = LargestInt(LargestUInt(-1) / 2);
/// Maximum unsigned integer value that can be stored in a Json::Value.
- static const LargestUInt maxLargestUInt;
+ static constexpr LargestUInt maxLargestUInt = LargestUInt(-1);
/// Minimum signed int value that can be stored in a Json::Value.
- static const Int minInt;
+ static constexpr Int minInt = Int(~(UInt(-1) / 2));
/// Maximum signed int value that can be stored in a Json::Value.
- static const Int maxInt;
+ static constexpr Int maxInt = Int(UInt(-1) / 2);
/// Maximum unsigned int value that can be stored in a Json::Value.
- static const UInt maxUInt;
+ static constexpr UInt maxUInt = UInt(-1);
#if defined(JSON_HAS_INT64)
/// Minimum signed 64 bits int value that can be stored in a Json::Value.
- static const Int64 minInt64;
+ static constexpr Int64 minInt64 = Int64(~(UInt64(-1) / 2));
/// Maximum signed 64 bits int value that can be stored in a Json::Value.
- static const Int64 maxInt64;
+ static constexpr Int64 maxInt64 = Int64(UInt64(-1) / 2);
/// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
- static const UInt64 maxUInt64;
+ static constexpr UInt64 maxUInt64 = UInt64(-1);
#endif // defined(JSON_HAS_INT64)
-
/// Default precision for real value for string representation.
- static const UInt defaultRealPrecision;
-
+ static constexpr UInt defaultRealPrecision = 17;
+ // The constant is hard-coded because some compiler have trouble
+ // converting Value::maxUInt64 to a double correctly (AIX/xlC).
+ // Assumes that UInt64 is a 64 bits integer.
+ static constexpr double maxUInt64AsDouble = 18446744073709551615.0;
// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler
// when using gcc and clang backend compilers. CZString
// cannot be defined as private. See issue #486
#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
public:
- /** \brief Create a default Value of the given type.
-
- This is a very useful constructor.
- To create an empty array, pass arrayValue.
- To create an empty object, pass objectValue.
- Another Value can then be set to this one by assignment.
-This is useful since clear() and resize() will not alter types.
-
- Examples:
-\code
-Json::Value null_value; // null
-Json::Value arr_value(Json::arrayValue); // []
-Json::Value obj_value(Json::objectValue); // {}
-\endcode
- */
+ /**
+ * \brief Create a default Value of the given type.
+ *
+ * This is a very useful constructor.
+ * To create an empty array, pass arrayValue.
+ * To create an empty object, pass objectValue.
+ * Another Value can then be set to this one by assignment.
+ * This is useful since clear() and resize() will not alter types.
+ *
+ * Examples:
+ * \code
+ * Json::Value null_value; // null
+ * Json::Value arr_value(Json::arrayValue); // []
+ * Json::Value obj_value(Json::objectValue); // {}
+ * \endcode
+ */
Value(ValueType type = nullValue);
Value(Int value);
Value(UInt value);
Value(double value);
Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
Value(const char* begin, const char* end); ///< Copy all, incl zeroes.
- /** \brief Constructs a value from a static string.
-
+ /**
+ * \brief Constructs a value from a static string.
+ *
* Like other value string constructor but do not duplicate the string for
- * internal storage. The given string must remain alive after the call to this
- * constructor.
+ * internal storage. The given string must remain alive after the call to
+ * this constructor.
+ *
* \note This works only for null-terminated strings. (We cannot change the
- * size of this class, so we have nowhere to store the length,
- * which might be computed later for various operations.)
+ * size of this class, so we have nowhere to store the length, which might be
+ * computed later for various operations.)
*
* Example of usage:
- * \code
- * static StaticString foo("some text");
- * Json::Value aValue(foo);
- * \endcode
+ * \code
+ * static StaticString foo("some text");
+ * Json::Value aValue(foo);
+ * \endcode
*/
Value(const StaticString& value);
- Value(const String& value); ///< Copy data() til size(). Embedded
- ///< zeroes too.
+ Value(const String& value);
#ifdef JSON_USE_CPPTL
Value(const CppTL::ConstString& value);
#endif
bool isArray() const;
bool isObject() const;
+ /// The `as<T>` and `is<T>` member function templates and specializations.
+ template <typename T> T as() const = delete;
+ template <typename T> bool is() const = delete;
+
bool isConvertibleTo(ValueType other) const;
/// Number of values in array or object
/// \post type() is arrayValue
void resize(ArrayIndex newSize);
- /// Access an array element (zero based index ).
- /// If the array contains less than index element, then null value are
- /// inserted
- /// in the array so that its size is index+1.
+ //@{
+ /// Access an array element (zero based index). If the array contains less
+ /// than index element, then null value are inserted in the array so that
+ /// its size is index+1.
/// (You may need to say 'value[0u]' to get your compiler to distinguish
- /// this from the operator[] which takes a string.)
+ /// this from the operator[] which takes a string.)
Value& operator[](ArrayIndex index);
-
- /// Access an array element (zero based index ).
- /// If the array contains less than index element, then null value are
- /// inserted
- /// in the array so that its size is index+1.
- /// (You may need to say 'value[0u]' to get your compiler to distinguish
- /// this from the operator[] which takes a string.)
Value& operator[](int index);
+ //@}
- /// Access an array element (zero based index )
+ //@{
+ /// Access an array element (zero based index).
/// (You may need to say 'value[0u]' to get your compiler to distinguish
- /// this from the operator[] which takes a string.)
+ /// this from the operator[] which takes a string.)
const Value& operator[](ArrayIndex index) const;
-
- /// Access an array element (zero based index )
- /// (You may need to say 'value[0u]' to get your compiler to distinguish
- /// this from the operator[] which takes a string.)
const Value& operator[](int index) const;
+ //@}
/// If the array contains at least index+1 elements, returns the element
- /// value,
- /// otherwise returns defaultValue.
+ /// value, otherwise returns defaultValue.
Value get(ArrayIndex index, const Value& defaultValue) const;
/// Return true if index < size().
bool isValidIndex(ArrayIndex index) const;
/// Equivalent to jsonvalue[jsonvalue.size()] = value;
Value& append(const Value& value);
Value& append(Value&& value);
+ /// \brief Insert value in array at specific index
+ bool insert(ArrayIndex index, Value newValue);
/// Access an object value by name, create a null member if it does not exist.
/// \note Because of our implementation, keys are limited to 2^30 -1 chars.
- /// Exceeding that will cause an exception.
+ /// Exceeding that will cause an exception.
Value& operator[](const char* key);
/// Access an object value by name, returns null if there is no member with
/// that name.
/// \param key may contain embedded nulls.
const Value& operator[](const String& key) const;
/** \brief Access an object value by name, create a null member if it does not
- exist.
-
+ * exist.
+ *
* If the object has no entry for that name, then the member name used to
- store
- * the new entry is not duplicated.
+ * store the new entry is not duplicated.
* Example of use:
- * \code
- * Json::Value object;
- * static const StaticString code("code");
- * object[code] = 1234;
- * \endcode
+ * \code
+ * Json::Value object;
+ * static const StaticString code("code");
+ * object[code] = 1234;
+ * \endcode
*/
Value& operator[](const StaticString& key);
#ifdef JSON_USE_CPPTL
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
/// \note key may contain embedded nulls.
- Value
- get(const char* begin, const char* end, const Value& defaultValue) const;
+ Value get(const char* begin, const char* end,
+ const Value& defaultValue) const;
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
/// \param key may contain embedded nulls.
/// but 'key' is null-terminated.
bool removeMember(const char* key, Value* removed);
/** \brief Remove the named map member.
-
- Update 'removed' iff removed.
- \param key may contain embedded nulls.
- \return true iff removed (no exceptions)
- */
+ *
+ * Update 'removed' iff removed.
+ * \param key may contain embedded nulls.
+ * \return true iff removed (no exceptions)
+ */
bool removeMember(String const& key, Value* removed);
/// Same as removeMember(String const& key, Value* removed)
bool removeMember(const char* begin, const char* end, Value* removed);
/** \brief Remove the indexed array element.
-
- O(n) expensive operations.
- Update 'removed' iff removed.
- \return true if removed (no exceptions)
- */
+ *
+ * O(n) expensive operations.
+ * Update 'removed' iff removed.
+ * \return true if removed (no exceptions)
+ */
bool removeIndex(ArrayIndex index, Value* removed);
/// Return true if the object has a member named key.
Comments& operator=(Comments&& that);
bool has(CommentPlacement slot) const;
String get(CommentPlacement slot) const;
- void set(CommentPlacement slot, String s);
+ void set(CommentPlacement slot, String comment);
private:
using Array = std::array<String, numberOfCommentPlacement>;
ptrdiff_t limit_;
};
+template <> inline bool Value::as<bool>() const { return asBool(); }
+template <> inline bool Value::is<bool>() const { return isBool(); }
+
+template <> inline Int Value::as<Int>() const { return asInt(); }
+template <> inline bool Value::is<Int>() const { return isInt(); }
+
+template <> inline UInt Value::as<UInt>() const { return asUInt(); }
+template <> inline bool Value::is<UInt>() const { return isUInt(); }
+
+#if defined(JSON_HAS_INT64)
+template <> inline Int64 Value::as<Int64>() const { return asInt64(); }
+template <> inline bool Value::is<Int64>() const { return isInt64(); }
+
+template <> inline UInt64 Value::as<UInt64>() const { return asUInt64(); }
+template <> inline bool Value::is<UInt64>() const { return isUInt64(); }
+#endif
+
+template <> inline double Value::as<double>() const { return asDouble(); }
+template <> inline bool Value::is<double>() const { return isDouble(); }
+
+template <> inline String Value::as<String>() const { return asString(); }
+template <> inline bool Value::is<String>() const { return isString(); }
+
+/// These `as` specializations are type conversions, and do not have a
+/// corresponding `is`.
+template <> inline float Value::as<float>() const { return asFloat(); }
+template <> inline const char* Value::as<const char*>() const {
+ return asCString();
+}
+#ifdef JSON_USE_CPPTL
+template <> inline CppTL::ConstString Value::as<CppTL::ConstString>() const {
+ return asConstString();
+}
+#endif
+
/** \brief Experimental and untested: represents an element of the "path" to
* access a node.
*/
PathArgument();
PathArgument(ArrayIndex index);
PathArgument(const char* key);
- PathArgument(const String& key);
+ PathArgument(String key);
private:
enum Kind { kindNone = 0, kindIndex, kindKey };
* - ".name1.name2.name3"
* - ".[0][1][2].name1[3]"
* - ".%" => member name is provided as parameter
- * - ".[%]" => index is provied as parameter
+ * - ".[%]" => index is provided as parameter
*/
class JSON_API Path {
public:
- Path(const String& path,
- const PathArgument& a1 = PathArgument(),
+ Path(const String& path, const PathArgument& a1 = PathArgument(),
const PathArgument& a2 = PathArgument(),
const PathArgument& a3 = PathArgument(),
const PathArgument& a4 = PathArgument(),
typedef std::vector<PathArgument> Args;
void makePath(const String& path, const InArgs& in);
- void addPathInArg(const String& path,
- const InArgs& in,
- InArgs::const_iterator& itInArg,
- PathArgument::Kind kind);
+ void addPathInArg(const String& path, const InArgs& in,
+ InArgs::const_iterator& itInArg, PathArgument::Kind kind);
static void invalidPath(const String& path, int location);
Args args_;
char const* memberName(char const** end) const;
protected:
- Value& deref() const;
+ /*! Internal utility functions to assist with implementing
+ * other iterator functions. The const and non-const versions
+ * of the "deref" protected methods expose the protected
+ * current_ member variable in a way that can often be
+ * optimized away by the compiler.
+ */
+ const Value& deref() const;
+ Value& deref();
void increment();
return *this;
}
- reference operator*() const { return deref(); }
-
- pointer operator->() const { return &deref(); }
+ /*! The return value of non-const iterators can be
+ * changed, so the these functions are not const
+ * because the returned references/pointers can be used
+ * to change state of the base class.
+ */
+ reference operator*() { return deref(); }
+ pointer operator->() { return &deref(); }
};
inline void swap(Value& a, Value& b) { a.swap(b); }
--- /dev/null
+#ifndef JSON_VERSION_H_INCLUDED
+#define JSON_VERSION_H_INCLUDED
+
+// Note: version must be updated in three places when doing a release. This
+// annoying process ensures that amalgamate, CMake, and meson all report the
+// correct version.
+// 1. /meson.build
+// 2. /include/json/version.h
+// 3. /CMakeLists.txt
+// IMPORTANT: also update the SOVERSION!!
+
+#define JSONCPP_VERSION_STRING "1.9.2"
+#define JSONCPP_VERSION_MAJOR 1
+#define JSONCPP_VERSION_MINOR 9
+#define JSONCPP_VERSION_PATCH 2
+#define JSONCPP_VERSION_QUALIFIER
+#define JSONCPP_VERSION_HEXA \
+ ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
+ (JSONCPP_VERSION_PATCH << 8))
+
+#ifdef JSONCPP_USING_SECURE_MEMORY
+#undef JSONCPP_USING_SECURE_MEMORY
+#endif
+#define JSONCPP_USING_SECURE_MEMORY 0
+// If non-zero, the library zeroes any memory that it has allocated before
+// it frees its memory.
+
+#endif // JSON_VERSION_H_INCLUDED
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
-*/
+ *
+ * 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:
OStream* sout_; // not owned; will not delete
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 (For now, we always return zero, so check the
- stream instead.) \throw std::exception possibly, depending on configuration
+ * Do not take ownership of sout, but maintain a reference during function.
+ * \pre sout != NULL
+ * \return zero on success (For now, we always return zero, so check the
+ * stream instead.) \throw std::exception possibly, depending on
+ * configuration
*/
virtual int write(Value const& root, OStream* sout) = 0;
/** \brief Build a StreamWriter implementation.
-Usage:
-\code
- using namespace Json;
- Value value = ...;
- StreamWriterBuilder builder;
- builder["commentStyle"] = "None";
- builder["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
+* Usage:
+* \code
+* using namespace Json;
+* Value value = ...;
+* StreamWriterBuilder builder;
+* builder["commentStyle"] = "None";
+* builder["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>".
- - Setting this to an empty string also omits newline characters.
- - "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.
- - "useSpecialFloats": false or true
- - If true, outputs non-finite floating point values in the following way:
- NaN values as "NaN", positive infinity as "Infinity", and negative
- infinity as "-Infinity".
- - "precision": int
- - Number of precision digits for formatting of real values.
- - "precisionType": "significant"(default) or "decimal"
- - Type of precision for formatting of real values.
-
- You can examine 'settings_` yourself
- to see the defaults. You can also write and read them just like any
- JSON Value.
- \sa setDefaults()
- */
+ * Available settings (case-sensitive):
+ * - "commentStyle": "None" or "All"
+ * - "indentation": "<anything>".
+ * - Setting this to an empty string also omits newline characters.
+ * - "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.
+ * - "useSpecialFloats": false or true
+ * - If true, outputs non-finite floating point values in the following way:
+ * NaN values as "NaN", positive infinity as "Infinity", and negative
+ * infinity as "-Infinity".
+ * - "precision": int
+ * - Number of precision digits for formatting of real values.
+ * - "precisionType": "significant"(default) or "decimal"
+ * - Type of precision for formatting of real values.
+
+ * 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();
#endif // if defined(JSON_HAS_INT64)
String JSON_API valueToString(LargestInt value);
String JSON_API valueToString(LargestUInt value);
-String JSON_API
-valueToString(double value,
- unsigned int precision = Value::defaultRealPrecision,
- PrecisionType precisionType = PrecisionType::significantDigits);
+String JSON_API valueToString(
+ double value, unsigned int precision = Value::defaultRealPrecision,
+ PrecisionType precisionType = PrecisionType::significantDigits);
String JSON_API valueToString(bool value);
String JSON_API valueToQuotedString(const char* value);
+++ /dev/null
-# Copyright 2010 Baptiste Lepilleur and The JsonCpp Authors
-# Distributed under MIT license, or public domain if desired and
-# recognized in your jurisdiction.
-# See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-"""Tag the sandbox for release, make source and doc tarballs.
-
-Requires Python 2.6
-
-Example of invocation (use to test the script):
-python makerelease.py --platform=msvc6,msvc71,msvc80,msvc90,mingw -ublep 0.6.0 0.7.0-dev
-
-When testing this script:
-python makerelease.py --force --retag --platform=msvc6,msvc71,msvc80,mingw -ublep test-0.6.0 test-0.6.1-dev
-
-Example of invocation when doing a release:
-python makerelease.py 0.5.0 0.6.0-dev
-
-Note: This was for Subversion. Now that we are in GitHub, we do not
-need to build versioned tarballs anymore, so makerelease.py is defunct.
-"""
-
-from __future__ import print_function
-import os.path
-import subprocess
-import sys
-import doxybuild
-import subprocess
-import xml.etree.ElementTree as ElementTree
-import shutil
-import urllib2
-import tempfile
-import os
-import time
-from devtools import antglob, fixeol, tarball
-import amalgamate
-
-SVN_ROOT = 'https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/'
-SVN_TAG_ROOT = SVN_ROOT + 'tags/jsoncpp'
-SCONS_LOCAL_URL = 'http://sourceforge.net/projects/scons/files/scons-local/1.2.0/scons-local-1.2.0.tar.gz/download'
-SOURCEFORGE_PROJECT = 'jsoncpp'
-
-def set_version(version):
- with open('version','wb') as f:
- f.write(version.strip())
-
-def rmdir_if_exist(dir_path):
- if os.path.isdir(dir_path):
- shutil.rmtree(dir_path)
-
-class SVNError(Exception):
- pass
-
-def svn_command(command, *args):
- cmd = ['svn', '--non-interactive', command] + list(args)
- print('Running:', ' '.join(cmd))
- process = subprocess.Popen(cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- stdout = process.communicate()[0]
- if process.returncode:
- error = SVNError('SVN command failed:\n' + stdout)
- error.returncode = process.returncode
- raise error
- return stdout
-
-def check_no_pending_commit():
- """Checks that there is no pending commit in the sandbox."""
- stdout = svn_command('status', '--xml')
- etree = ElementTree.fromstring(stdout)
- msg = []
- for entry in etree.getiterator('entry'):
- path = entry.get('path')
- status = entry.find('wc-status').get('item')
- if status != 'unversioned' and path != 'version':
- msg.append('File "%s" has pending change (status="%s")' % (path, status))
- if msg:
- msg.insert(0, 'Pending change to commit found in sandbox. Commit them first!')
- return '\n'.join(msg)
-
-def svn_join_url(base_url, suffix):
- if not base_url.endswith('/'):
- base_url += '/'
- if suffix.startswith('/'):
- suffix = suffix[1:]
- return base_url + suffix
-
-def svn_check_if_tag_exist(tag_url):
- """Checks if a tag exist.
- Returns: True if the tag exist, False otherwise.
- """
- try:
- list_stdout = svn_command('list', tag_url)
- except SVNError as e:
- if e.returncode != 1 or not str(e).find('tag_url'):
- raise e
- # otherwise ignore error, meaning tag does not exist
- return False
- return True
-
-def svn_commit(message):
- """Commit the sandbox, providing the specified comment.
- """
- svn_command('ci', '-m', message)
-
-def svn_tag_sandbox(tag_url, message):
- """Makes a tag based on the sandbox revisions.
- """
- svn_command('copy', '-m', message, '.', tag_url)
-
-def svn_remove_tag(tag_url, message):
- """Removes an existing tag.
- """
- svn_command('delete', '-m', message, tag_url)
-
-def svn_export(tag_url, export_dir):
- """Exports the tag_url revision to export_dir.
- Target directory, including its parent is created if it does not exist.
- If the directory export_dir exist, it is deleted before export proceed.
- """
- rmdir_if_exist(export_dir)
- svn_command('export', tag_url, export_dir)
-
-def fix_sources_eol(dist_dir):
- """Set file EOL for tarball distribution.
- """
- print('Preparing exported source file EOL for distribution...')
- prune_dirs = antglob.prune_dirs + 'scons-local* ./build* ./libs ./dist'
- win_sources = antglob.glob(dist_dir,
- includes = '**/*.sln **/*.vcproj',
- prune_dirs = prune_dirs)
- unix_sources = antglob.glob(dist_dir,
- includes = '''**/*.h **/*.cpp **/*.inl **/*.txt **/*.dox **/*.py **/*.html **/*.in
- sconscript *.json *.expected AUTHORS LICENSE''',
- excludes = antglob.default_excludes + 'scons.py sconsign.py scons-*',
- prune_dirs = prune_dirs)
- for path in win_sources:
- fixeol.fix_source_eol(path, is_dry_run = False, verbose = True, eol = '\r\n')
- for path in unix_sources:
- fixeol.fix_source_eol(path, is_dry_run = False, verbose = True, eol = '\n')
-
-def download(url, target_path):
- """Download file represented by url to target_path.
- """
- f = urllib2.urlopen(url)
- try:
- data = f.read()
- finally:
- f.close()
- fout = open(target_path, 'wb')
- try:
- fout.write(data)
- finally:
- fout.close()
-
-def check_compile(distcheck_top_dir, platform):
- cmd = [sys.executable, 'scons.py', 'platform=%s' % platform, 'check']
- print('Running:', ' '.join(cmd))
- log_path = os.path.join(distcheck_top_dir, 'build-%s.log' % platform)
- flog = open(log_path, 'wb')
- try:
- process = subprocess.Popen(cmd,
- stdout=flog,
- stderr=subprocess.STDOUT,
- cwd=distcheck_top_dir)
- stdout = process.communicate()[0]
- status = (process.returncode == 0)
- finally:
- flog.close()
- return (status, log_path)
-
-def write_tempfile(content, **kwargs):
- fd, path = tempfile.mkstemp(**kwargs)
- f = os.fdopen(fd, 'wt')
- try:
- f.write(content)
- finally:
- f.close()
- return path
-
-class SFTPError(Exception):
- pass
-
-def run_sftp_batch(userhost, sftp, batch, retry=0):
- path = write_tempfile(batch, suffix='.sftp', text=True)
- # psftp -agent -C blep,jsoncpp@web.sourceforge.net -batch -b batch.sftp -bc
- cmd = [sftp, '-agent', '-C', '-batch', '-b', path, '-bc', userhost]
- error = None
- for retry_index in range(0, max(1,retry)):
- heading = retry_index == 0 and 'Running:' or 'Retrying:'
- print(heading, ' '.join(cmd))
- process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- stdout = process.communicate()[0]
- if process.returncode != 0:
- error = SFTPError('SFTP batch failed:\n' + stdout)
- else:
- break
- if error:
- raise error
- return stdout
-
-def sourceforge_web_synchro(sourceforge_project, doc_dir,
- user=None, sftp='sftp'):
- """Notes: does not synchronize sub-directory of doc-dir.
- """
- userhost = '%s,%s@web.sourceforge.net' % (user, sourceforge_project)
- stdout = run_sftp_batch(userhost, sftp, """
-cd htdocs
-dir
-exit
-""")
- existing_paths = set()
- collect = 0
- for line in stdout.split('\n'):
- line = line.strip()
- if not collect and line.endswith('> dir'):
- collect = True
- elif collect and line.endswith('> exit'):
- break
- elif collect == 1:
- collect = 2
- elif collect == 2:
- path = line.strip().split()[-1:]
- if path and path[0] not in ('.', '..'):
- existing_paths.add(path[0])
- upload_paths = set([os.path.basename(p) for p in antglob.glob(doc_dir)])
- paths_to_remove = existing_paths - upload_paths
- if paths_to_remove:
- print('Removing the following file from web:')
- print('\n'.join(paths_to_remove))
- stdout = run_sftp_batch(userhost, sftp, """cd htdocs
-rm %s
-exit""" % ' '.join(paths_to_remove))
- print('Uploading %d files:' % len(upload_paths))
- batch_size = 10
- upload_paths = list(upload_paths)
- start_time = time.time()
- for index in range(0,len(upload_paths),batch_size):
- paths = upload_paths[index:index+batch_size]
- file_per_sec = (time.time() - start_time) / (index+1)
- remaining_files = len(upload_paths) - index
- remaining_sec = file_per_sec * remaining_files
- print('%d/%d, ETA=%.1fs' % (index+1, len(upload_paths), remaining_sec))
- run_sftp_batch(userhost, sftp, """cd htdocs
-lcd %s
-mput %s
-exit""" % (doc_dir, ' '.join(paths)), retry=3)
-
-def sourceforge_release_tarball(sourceforge_project, paths, user=None, sftp='sftp'):
- userhost = '%s,%s@frs.sourceforge.net' % (user, sourceforge_project)
- run_sftp_batch(userhost, sftp, """
-mput %s
-exit
-""" % (' '.join(paths),))
-
-
-def main():
- usage = """%prog release_version next_dev_version
-Update 'version' file to release_version and commit.
-Generates the document tarball.
-Tags the sandbox revision with release_version.
-Update 'version' file to next_dev_version and commit.
-
-Performs an svn export of tag release version, and build a source tarball.
-
-Must be started in the project top directory.
-
-Warning: --force should only be used when developing/testing the release script.
-"""
- from optparse import OptionParser
- parser = OptionParser(usage=usage)
- parser.allow_interspersed_args = False
- parser.add_option('--dot', dest="dot_path", action='store', default=doxybuild.find_program('dot'),
- help="""Path to GraphViz dot tool. Must be full qualified path. [Default: %default]""")
- parser.add_option('--doxygen', dest="doxygen_path", action='store', default=doxybuild.find_program('doxygen'),
- help="""Path to Doxygen tool. [Default: %default]""")
- parser.add_option('--force', dest="ignore_pending_commit", action='store_true', default=False,
- help="""Ignore pending commit. [Default: %default]""")
- parser.add_option('--retag', dest="retag_release", action='store_true', default=False,
- help="""Overwrite release existing tag if it exist. [Default: %default]""")
- parser.add_option('-p', '--platforms', dest="platforms", action='store', default='',
- help="""Comma separated list of platform passed to scons for build check.""")
- parser.add_option('--no-test', dest="no_test", action='store_true', default=False,
- help="""Skips build check.""")
- parser.add_option('--no-web', dest="no_web", action='store_true', default=False,
- help="""Do not update web site.""")
- parser.add_option('-u', '--upload-user', dest="user", action='store',
- help="""Sourceforge user for SFTP documentation upload.""")
- parser.add_option('--sftp', dest='sftp', action='store', default=doxybuild.find_program('psftp', 'sftp'),
- help="""Path of the SFTP compatible binary used to upload the documentation.""")
- parser.enable_interspersed_args()
- options, args = parser.parse_args()
-
- if len(args) != 2:
- parser.error('release_version missing on command-line.')
- release_version = args[0]
- next_version = args[1]
-
- if not options.platforms and not options.no_test:
- parser.error('You must specify either --platform or --no-test option.')
-
- if options.ignore_pending_commit:
- msg = ''
- else:
- msg = check_no_pending_commit()
- if not msg:
- print('Setting version to', release_version)
- set_version(release_version)
- svn_commit('Release ' + release_version)
- tag_url = svn_join_url(SVN_TAG_ROOT, release_version)
- if svn_check_if_tag_exist(tag_url):
- if options.retag_release:
- svn_remove_tag(tag_url, 'Overwriting previous tag')
- else:
- print('Aborting, tag %s already exist. Use --retag to overwrite it!' % tag_url)
- sys.exit(1)
- svn_tag_sandbox(tag_url, 'Release ' + release_version)
-
- print('Generated doxygen document...')
-## doc_dirname = r'jsoncpp-api-html-0.5.0'
-## doc_tarball_path = r'e:\prg\vc\Lib\jsoncpp-trunk\dist\jsoncpp-api-html-0.5.0.tar.gz'
- doc_tarball_path, doc_dirname = doxybuild.build_doc(options, make_release=True)
- doc_distcheck_dir = 'dist/doccheck'
- tarball.decompress(doc_tarball_path, doc_distcheck_dir)
- doc_distcheck_top_dir = os.path.join(doc_distcheck_dir, doc_dirname)
-
- export_dir = 'dist/export'
- svn_export(tag_url, export_dir)
- fix_sources_eol(export_dir)
-
- source_dir = 'jsoncpp-src-' + release_version
- source_tarball_path = 'dist/%s.tar.gz' % source_dir
- print('Generating source tarball to', source_tarball_path)
- tarball.make_tarball(source_tarball_path, [export_dir], export_dir, prefix_dir=source_dir)
-
- amalgamation_tarball_path = 'dist/%s-amalgamation.tar.gz' % source_dir
- print('Generating amalgamation source tarball to', amalgamation_tarball_path)
- amalgamation_dir = 'dist/amalgamation'
- amalgamate.amalgamate_source(export_dir, '%s/jsoncpp.cpp' % amalgamation_dir, 'json/json.h')
- amalgamation_source_dir = 'jsoncpp-src-amalgamation' + release_version
- tarball.make_tarball(amalgamation_tarball_path, [amalgamation_dir],
- amalgamation_dir, prefix_dir=amalgamation_source_dir)
-
- # Decompress source tarball, download and install scons-local
- distcheck_dir = 'dist/distcheck'
- distcheck_top_dir = distcheck_dir + '/' + source_dir
- print('Decompressing source tarball to', distcheck_dir)
- rmdir_if_exist(distcheck_dir)
- tarball.decompress(source_tarball_path, distcheck_dir)
- scons_local_path = 'dist/scons-local.tar.gz'
- print('Downloading scons-local to', scons_local_path)
- download(SCONS_LOCAL_URL, scons_local_path)
- print('Decompressing scons-local to', distcheck_top_dir)
- tarball.decompress(scons_local_path, distcheck_top_dir)
-
- # Run compilation
- print('Compiling decompressed tarball')
- all_build_status = True
- for platform in options.platforms.split(','):
- print('Testing platform:', platform)
- build_status, log_path = check_compile(distcheck_top_dir, platform)
- print('see build log:', log_path)
- print(build_status and '=> ok' or '=> FAILED')
- all_build_status = all_build_status and build_status
- if not build_status:
- print('Testing failed on at least one platform, aborting...')
- svn_remove_tag(tag_url, 'Removing tag due to failed testing')
- sys.exit(1)
- if options.user:
- if not options.no_web:
- print('Uploading documentation using user', options.user)
- sourceforge_web_synchro(SOURCEFORGE_PROJECT, doc_distcheck_top_dir, user=options.user, sftp=options.sftp)
- print('Completed documentation upload')
- print('Uploading source and documentation tarballs for release using user', options.user)
- sourceforge_release_tarball(SOURCEFORGE_PROJECT,
- [source_tarball_path, doc_tarball_path],
- user=options.user, sftp=options.sftp)
- print('Source and doc release tarballs uploaded')
- else:
- print('No upload user specified. Web site and download tarball were not uploaded.')
- print('Tarball can be found at:', doc_tarball_path)
-
- # Set next version number and commit
- set_version(next_version)
- svn_commit('Released ' + release_version)
- else:
- sys.stderr.write(msg + '\n')
-
-if __name__ == '__main__':
- main()
project(
'jsoncpp',
'cpp',
- version : '1.9.0',
+
+ # Note: version must be updated in three places when doing a release. This
+ # annoying process ensures that amalgamate, CMake, and meson all report the
+ # correct version.
+ # 1. /meson.build
+ # 2. /include/json/version.h
+ # 3. /CMakeLists.txt
+ # IMPORTANT: also update the SOVERSION!!
+ version : '1.9.2',
default_options : [
'buildtype=release',
'cpp_std=c++11',
'warning_level=1'],
license : 'Public Domain',
- meson_version : '>= 0.50.0')
-
-jsoncpp_ver_arr = meson.project_version().split('.')
-jsoncpp_major_version = jsoncpp_ver_arr[0]
-jsoncpp_minor_version = jsoncpp_ver_arr[1]
-jsoncpp_patch_version = jsoncpp_ver_arr[2]
+ meson_version : '>= 0.49.0')
-jsoncpp_cdata = configuration_data()
-jsoncpp_cdata.set('JSONCPP_VERSION', meson.project_version())
-jsoncpp_cdata.set('JSONCPP_VERSION_MAJOR', jsoncpp_major_version)
-jsoncpp_cdata.set('JSONCPP_VERSION_MINOR', jsoncpp_minor_version)
-jsoncpp_cdata.set('JSONCPP_VERSION_PATCH', jsoncpp_patch_version)
-jsoncpp_cdata.set('JSONCPP_USE_SECURE_MEMORY',0)
-
-jsoncpp_gen_sources = configure_file(
- input : 'src/lib_json/version.h.in',
- output : 'version.h',
- configuration : jsoncpp_cdata,
- install : true,
- install_dir : join_paths(get_option('prefix'), get_option('includedir'), 'json')
-)
jsoncpp_headers = [
'include/json/allocator.h',
'include/json/assertions.h',
'include/json/autolink.h',
'include/json/config.h',
- 'include/json/features.h',
+ 'include/json/json_features.h',
'include/json/forwards.h',
'include/json/json.h',
'include/json/reader.h',
'include/json/value.h',
+ 'include/json/version.h',
'include/json/writer.h']
jsoncpp_include_directories = include_directories('include')
jsoncpp_lib = library(
'jsoncpp',
- [ jsoncpp_gen_sources,
- jsoncpp_headers,
+ [ jsoncpp_headers,
'src/lib_json/json_tool.h',
'src/lib_json/json_reader.cpp',
'src/lib_json/json_value.cpp',
'src/lib_json/json_writer.cpp'],
- soversion : 21,
+ soversion : 22,
install : true,
include_directories : jsoncpp_include_directories,
cpp_args: dll_export_flag)
include_directories : jsoncpp_include_directories,
link_with : jsoncpp_lib,
version : meson.project_version(),
- sources : jsoncpp_gen_sources)
+ )
# tests
-python = import('python3').find_python()
+python = import('python').find_installation()
jsoncpp_test = executable(
'jsoncpp_test',
-libdir=@CMAKE_INSTALL_FULL_LIBDIR@
-includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: jsoncpp
Description: A C++ library for interacting with JSON
-find_package(PythonInterp 2.6)
+if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
+ # The new Python3 module is much more robust than the previous PythonInterp
+ find_package (Python3 COMPONENTS Interpreter)
+ # Set variables for backwards compatibility with cmake < 3.12.0
+ set(PYTHONINTERP_FOUND ${Python3_Interpreter_FOUND})
+ set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE})
+else()
+ set(Python_ADDITIONAL_VERSIONS 3.8)
+ find_package(PythonInterp 3)
+endif()
add_executable(jsontestrunner_exe
main.cpp
)
if(BUILD_SHARED_LIBS)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
add_compile_definitions( JSON_DLL )
+ else()
+ add_definitions( -DJSON_DLL )
+ endif()
endif()
target_link_libraries(jsontestrunner_exe jsoncpp_lib)
#include <algorithm> // sort
#include <cstdio>
+#include <iostream>
#include <json/json.h>
+#include <memory>
#include <sstream>
struct Options {
return text;
}
-static void
-printValueTree(FILE* fout, Json::Value& value, const Json::String& path = ".") {
+static void printValueTree(FILE* fout, Json::Value& value,
+ const Json::String& path = ".") {
if (value.hasComment(Json::commentBefore)) {
fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
}
static int parseAndSaveValueTree(const Json::String& input,
const Json::String& actual,
const Json::String& kind,
- const Json::Features& features,
- bool parseOnly,
- Json::Value* root) {
- Json::Reader reader(features);
- bool parsingSuccessful =
- reader.parse(input.data(), input.data() + input.size(), *root);
- if (!parsingSuccessful) {
- printf("Failed to parse %s file: \n%s\n", kind.c_str(),
- reader.getFormattedErrorMessages().c_str());
- return 1;
+ const Json::Features& features, bool parseOnly,
+ Json::Value* root, bool use_legacy) {
+ if (!use_legacy) {
+ Json::CharReaderBuilder builder;
+
+ builder.settings_["allowComments"] = features.allowComments_;
+ builder.settings_["strictRoot"] = features.strictRoot_;
+ builder.settings_["allowDroppedNullPlaceholders"] =
+ features.allowDroppedNullPlaceholders_;
+ builder.settings_["allowNumericKeys"] = features.allowNumericKeys_;
+
+ std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
+ Json::String errors;
+ const bool parsingSuccessful =
+ reader->parse(input.data(), input.data() + input.size(), root, &errors);
+
+ if (!parsingSuccessful) {
+ std::cerr << "Failed to parse " << kind << " file: " << std::endl
+ << errors << std::endl;
+ return 1;
+ }
+
+ // We may instead check the legacy implementation (to ensure it doesn't
+ // randomly get broken).
+ } else {
+ Json::Reader reader(features);
+ const bool parsingSuccessful =
+ reader.parse(input.data(), input.data() + input.size(), *root);
+ if (!parsingSuccessful) {
+ std::cerr << "Failed to parse " << kind << " file: " << std::endl
+ << reader.getFormatedErrorMessages() << std::endl;
+ return 1;
+ }
}
+
if (!parseOnly) {
FILE* factual = fopen(actual.c_str(), "wt");
if (!factual) {
- printf("Failed to create %s actual file.\n", kind.c_str());
+ std::cerr << "Failed to create '" << kind << "' actual file."
+ << std::endl;
return 2;
}
printValueTree(factual, *root);
*rewrite = write(root);
FILE* fout = fopen(rewritePath.c_str(), "wt");
if (!fout) {
- printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
+ std::cerr << "Failed to create rewrite file: " << rewritePath << std::endl;
return 2;
}
fprintf(fout, "%s\n", rewrite->c_str());
static void printConfig() {
// Print the configuration used to compile JsonCpp
#if defined(JSON_NO_INT64)
- printf("JSON_NO_INT64=1\n");
+ std::cout << "JSON_NO_INT64=1" << std::endl;
#else
- printf("JSON_NO_INT64=0\n");
+ std::cout << "JSON_NO_INT64=0" << std::endl;
#endif
}
static int printUsage(const char* argv[]) {
- printf("Usage: %s [--strict] input-json-file", argv[0]);
+ std::cout << "Usage: " << argv[0] << " [--strict] input-json-file"
+ << std::endl;
return 3;
}
} else if (writerName == "BuiltStyledStreamWriter") {
opts->write = &useBuiltStyledStreamWriter;
} else {
- printf("Unknown '--json-writer %s'\n", writerName.c_str());
+ std::cerr << "Unknown '--json-writer' " << writerName << std::endl;
return 4;
}
}
opts->path = argv[index];
return 0;
}
-static int runTest(Options const& opts) {
+
+static int runTest(Options const& opts, bool use_legacy) {
int exitCode = 0;
Json::String input = readInputTestFile(opts.path.c_str());
if (input.empty()) {
- printf("Failed to read input or empty input: %s\n", opts.path.c_str());
+ std::cerr << "Invalid input file: " << opts.path << std::endl;
return 3;
}
Json::String basePath = removeSuffix(opts.path, ".json");
if (!opts.parseOnly && basePath.empty()) {
- printf("Bad input path. Path does not end with '.expected':\n%s\n",
- opts.path.c_str());
+ std::cerr << "Bad input path '" << opts.path
+ << "'. Must end with '.expected'" << std::endl;
return 3;
}
Json::Value root;
exitCode = parseAndSaveValueTree(input, actualPath, "input", opts.features,
- opts.parseOnly, &root);
+ opts.parseOnly, &root, use_legacy);
if (exitCode || opts.parseOnly) {
return exitCode;
}
+
Json::String rewrite;
exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
if (exitCode) {
return exitCode;
}
+
Json::Value rewriteRoot;
exitCode = parseAndSaveValueTree(rewrite, rewriteActualPath, "rewrite",
- opts.features, opts.parseOnly, &rewriteRoot);
- if (exitCode) {
- return exitCode;
- }
- return 0;
+ opts.features, opts.parseOnly, &rewriteRoot,
+ use_legacy);
+
+ return exitCode;
}
+
int main(int argc, const char* argv[]) {
Options opts;
try {
int exitCode = parseCommandLine(argc, argv, &opts);
if (exitCode != 0) {
- printf("Failed to parse command-line.");
+ std::cerr << "Failed to parse command-line." << std::endl;
return exitCode;
}
- return runTest(opts);
+
+ const int modern_return_code = runTest(opts, false);
+ if (modern_return_code) {
+ return modern_return_code;
+ }
+
+ const std::string filename =
+ opts.path.substr(opts.path.find_last_of("\\/") + 1);
+ const bool should_run_legacy = (filename.rfind("legacy_", 0) == 0);
+ if (should_run_legacy) {
+ return runTest(opts, true);
+ }
} catch (const std::exception& e) {
- printf("Unhandled exception:\n%s\n", e.what());
+ std::cerr << "Unhandled exception:" << std::endl << e.what() << std::endl;
return 1;
}
}
if(NOT (HAVE_CLOCALE AND HAVE_LCONV_SIZE AND HAVE_DECIMAL_POINT AND HAVE_LOCALECONV))
message(WARNING "Locale functionality is not supported")
- add_compile_definitions(JSONCPP_NO_LOCALE_SUPPORT)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
+ add_compile_definitions(JSONCPP_NO_LOCALE_SUPPORT)
+ else()
+ add_definitions(-DJSONCPP_NO_LOCALE_SUPPORT)
+ endif()
endif()
set( JSONCPP_INCLUDE_DIR ../../include )
set( PUBLIC_HEADERS
${JSONCPP_INCLUDE_DIR}/json/config.h
${JSONCPP_INCLUDE_DIR}/json/forwards.h
- ${JSONCPP_INCLUDE_DIR}/json/features.h
+ ${JSONCPP_INCLUDE_DIR}/json/json_features.h
${JSONCPP_INCLUDE_DIR}/json/value.h
${JSONCPP_INCLUDE_DIR}/json/reader.h
+ ${JSONCPP_INCLUDE_DIR}/json/version.h
${JSONCPP_INCLUDE_DIR}/json/writer.h
${JSONCPP_INCLUDE_DIR}/json/assertions.h
- ${PROJECT_BINARY_DIR}/include/json/version.h
)
source_group( "Public API" FILES ${PUBLIC_HEADERS} )
json_reader.cpp
json_valueiterator.inl
json_value.cpp
- json_writer.cpp
- version.h.in)
+ json_writer.cpp)
# Install instructions for this target
if(JSONCPP_WITH_CMAKE_PACKAGE)
set(INSTALL_EXPORT)
endif()
+
if(BUILD_SHARED_LIBS)
- add_compile_definitions( JSON_DLL_BUILD )
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
+ add_compile_definitions( JSON_DLL_BUILD )
+ else()
+ add_definitions( -DJSON_DLL_BUILD )
+ endif()
endif()
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <cassert>
#include <cstring>
+#include <iostream>
#include <istream>
#include <limits>
#include <memory>
namespace Json {
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
-typedef std::unique_ptr<CharReader> CharReaderPtr;
+using CharReaderPtr = std::unique_ptr<CharReader>;
#else
typedef std::auto_ptr<CharReader> CharReaderPtr;
#endif
// Class Reader
// //////////////////////////////////////////////////////////////////
-Reader::Reader()
- : errors_(), document_(), commentsBefore_(), features_(Features::all()) {}
+Reader::Reader() : features_(Features::all()) {}
-Reader::Reader(const Features& features)
- : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
- lastValue_(), commentsBefore_(), features_(features), collectComments_() {
-}
+Reader::Reader(const Features& features) : features_(features) {}
-bool Reader::parse(const std::string& document,
- Value& root,
+bool Reader::parse(const std::string& document, Value& root,
bool collectComments) {
document_.assign(document.begin(), document.end());
const char* begin = document_.c_str();
// Since String is reference-counted, this at least does not
// create an extra copy.
String doc;
- std::getline(is, doc, (char)EOF);
+ std::getline(is, doc, static_cast<char> EOF);
return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
}
-bool Reader::parse(const char* beginDoc,
- const char* endDoc,
- Value& root,
+bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,
bool collectComments) {
if (!features_.allowComments_) {
collectComments = false;
if (!ok)
token.type_ = tokenError;
token.end_ = current_;
- return true;
+ return ok;
}
void Reader::skipSpaces() {
}
}
-bool Reader::match(Location pattern, int patternLength) {
+bool Reader::match(const Char* pattern, int patternLength) {
if (end_ - current_ < patternLength)
return false;
int index = patternLength;
return normalized;
}
-void Reader::addComment(Location begin,
- Location end,
+void Reader::addComment(Location begin, Location end,
CommentPlacement placement) {
assert(collectComments_);
const String& normalized = normalizeEOL(begin, end);
}
void Reader::readNumber() {
- const char* p = current_;
+ Location p = current_;
char c = '0'; // stopgap for already consumed character
// integral part
while (c >= '0' && c <= '9')
return true;
}
-bool Reader::decodeUnicodeCodePoint(Token& token,
- Location& current,
- Location end,
- unsigned int& unicode) {
+bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
+ Location end, unsigned int& unicode) {
if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
return false;
return true;
}
-bool Reader::decodeUnicodeEscapeSequence(Token& token,
- Location& current,
+bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
Location end,
unsigned int& ret_unicode) {
if (end - current < 4)
return false;
}
-bool Reader::addErrorAndRecover(const String& message,
- Token& token,
+bool Reader::addErrorAndRecover(const String& message, Token& token,
TokenType skipUntilToken) {
addError(message, token);
return recoverFromError(skipUntilToken);
return *current_++;
}
-void Reader::getLocationLineAndColumn(Location location,
- int& line,
+void Reader::getLocationLineAndColumn(Location location, int& line,
int& column) const {
Location current = begin_;
Location lastLineStart = current;
return true;
}
-bool Reader::pushError(const Value& value,
- const String& message,
+bool Reader::pushError(const Value& value, const String& message,
const Value& extra) {
ptrdiff_t const length = end_ - begin_;
if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
// for implementing JSON reading.
class OurReader {
public:
- typedef char Char;
- typedef const Char* Location;
+ using Char = char;
+ using Location = const Char*;
struct StructuredError {
ptrdiff_t offset_start;
ptrdiff_t offset_limit;
String message;
};
- OurReader(OurFeatures const& features);
- bool parse(const char* beginDoc,
- const char* endDoc,
- Value& root,
+ explicit OurReader(OurFeatures const& features);
+ bool parse(const char* beginDoc, const char* endDoc, Value& root,
bool collectComments = true);
String getFormattedErrorMessages() const;
std::vector<StructuredError> getStructuredErrors() const;
- bool pushError(const Value& value, const String& message);
- bool pushError(const Value& value, const String& message, const Value& extra);
- bool good() const;
private:
OurReader(OurReader const&); // no impl
Location extra_;
};
- typedef std::deque<ErrorInfo> Errors;
+ using Errors = std::deque<ErrorInfo>;
bool readToken(Token& token);
void skipSpaces();
- bool match(Location pattern, int patternLength);
+ bool match(const Char* pattern, int patternLength);
bool readComment();
- bool readCStyleComment();
+ bool readCStyleComment(bool* containsNewLineResult);
bool readCppStyleComment();
bool readString();
bool readStringSingleQuote();
bool decodeString(Token& token, String& decoded);
bool decodeDouble(Token& token);
bool decodeDouble(Token& token, Value& decoded);
- bool decodeUnicodeCodePoint(Token& token,
- Location& current,
- Location end,
+ bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
unsigned int& unicode);
- bool decodeUnicodeEscapeSequence(Token& token,
- Location& current,
- Location end,
- unsigned int& unicode);
+ bool decodeUnicodeEscapeSequence(Token& token, Location& current,
+ Location end, unsigned int& unicode);
bool addError(const String& message, Token& token, Location extra = nullptr);
bool recoverFromError(TokenType skipUntilToken);
- bool addErrorAndRecover(const String& message,
- Token& token,
+ bool addErrorAndRecover(const String& message, Token& token,
TokenType skipUntilToken);
void skipUntilSpace();
Value& currentValue();
Char getNextChar();
- void
- getLocationLineAndColumn(Location location, int& line, int& column) const;
+ void getLocationLineAndColumn(Location location, int& line,
+ int& column) const;
String getLocationLineAndColumn(Location location) const;
void addComment(Location begin, Location end, CommentPlacement placement);
void skipCommentTokens(Token& token);
static String normalizeEOL(Location begin, Location end);
static bool containsNewLine(Location begin, Location end);
- typedef std::stack<Value*> Nodes;
- Nodes nodes_;
- Errors errors_;
- String document_;
- Location begin_;
- Location end_;
- Location current_;
- Location lastValueEnd_;
- Value* lastValue_;
- String commentsBefore_;
+ using Nodes = std::stack<Value*>;
+
+ Nodes nodes_{};
+ Errors errors_{};
+ String document_{};
+ Location begin_ = nullptr;
+ Location end_ = nullptr;
+ Location current_ = nullptr;
+ Location lastValueEnd_ = nullptr;
+ Value* lastValue_ = nullptr;
+ bool lastValueHasAComment_ = false;
+ String commentsBefore_{};
OurFeatures const features_;
- bool collectComments_;
+ bool collectComments_ = false;
}; // OurReader
// complete copy of Read impl, for OurReader
return false;
}
-OurReader::OurReader(OurFeatures const& features)
- : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
- lastValue_(), commentsBefore_(), features_(features), collectComments_() {
-}
+OurReader::OurReader(OurFeatures const& features) : features_(features) {}
-bool OurReader::parse(const char* beginDoc,
- const char* endDoc,
- Value& root,
+bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
bool collectComments) {
if (!features_.allowComments_) {
collectComments = false;
nodes_.pop();
Token token;
skipCommentTokens(token);
- if (features_.failIfExtra_) {
- if ((features_.strictRoot_ || token.type_ != tokenError) &&
- token.type_ != tokenEndOfStream) {
- addError("Extra non-whitespace after JSON value.", token);
- return false;
- }
+ if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
+ addError("Extra non-whitespace after JSON value.", token);
+ return false;
}
if (collectComments_ && !commentsBefore_.empty())
root.setComment(commentsBefore_, commentAfter);
if (collectComments_) {
lastValueEnd_ = current_;
+ lastValueHasAComment_ = false;
lastValue_ = ¤tValue();
}
ok = features_.allowSpecialFloats_ && match("nfinity", 7);
}
break;
+ case '+':
+ if (readNumber(true)) {
+ token.type_ = tokenNumber;
+ } else {
+ token.type_ = tokenPosInf;
+ ok = features_.allowSpecialFloats_ && match("nfinity", 7);
+ }
+ break;
case 't':
token.type_ = tokenTrue;
ok = match("rue", 3);
if (!ok)
token.type_ = tokenError;
token.end_ = current_;
- return true;
+ return ok;
}
void OurReader::skipSpaces() {
}
}
-bool OurReader::match(Location pattern, int patternLength) {
+bool OurReader::match(const Char* pattern, int patternLength) {
if (end_ - current_ < patternLength)
return false;
int index = patternLength;
}
bool OurReader::readComment() {
- Location commentBegin = current_ - 1;
- Char c = getNextChar();
+ const Location commentBegin = current_ - 1;
+ const Char c = getNextChar();
bool successful = false;
- if (c == '*')
- successful = readCStyleComment();
- else if (c == '/')
+ bool cStyleWithEmbeddedNewline = false;
+
+ const bool isCStyleComment = (c == '*');
+ const bool isCppStyleComment = (c == '/');
+ if (isCStyleComment) {
+ successful = readCStyleComment(&cStyleWithEmbeddedNewline);
+ } else if (isCppStyleComment) {
successful = readCppStyleComment();
+ }
+
if (!successful)
return false;
if (collectComments_) {
CommentPlacement placement = commentBefore;
- if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
- if (c != '*' || !containsNewLine(commentBegin, current_))
- placement = commentAfterOnSameLine;
+
+ if (!lastValueHasAComment_) {
+ if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
+ if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
+ placement = commentAfterOnSameLine;
+ lastValueHasAComment_ = true;
+ }
+ }
}
addComment(commentBegin, current_, placement);
return normalized;
}
-void OurReader::addComment(Location begin,
- Location end,
+void OurReader::addComment(Location begin, Location end,
CommentPlacement placement) {
assert(collectComments_);
const String& normalized = normalizeEOL(begin, end);
}
}
-bool OurReader::readCStyleComment() {
+bool OurReader::readCStyleComment(bool* containsNewLineResult) {
+ *containsNewLineResult = false;
+
while ((current_ + 1) < end_) {
Char c = getNextChar();
- if (c == '*' && *current_ == '/')
+ if (c == '*' && *current_ == '/') {
break;
+ } else if (c == '\n') {
+ *containsNewLineResult = true;
+ }
}
+
return getNextChar() == '/';
}
}
bool OurReader::readNumber(bool checkInf) {
- const char* p = current_;
+ Location p = current_;
if (checkInf && p != end_ && *p == 'I') {
current_ = ++p;
return false;
// larger than the maximum supported value of an integer then
// we decode the number as a double.
Location current = token.start_;
- bool isNegative = *current == '-';
- if (isNegative)
+ const bool isNegative = *current == '-';
+ if (isNegative) {
++current;
+ }
- // TODO(issue #960): Change to constexpr
- static const auto positive_threshold = Value::maxLargestUInt / 10;
- static const auto positive_last_digit = Value::maxLargestUInt % 10;
- static const auto negative_threshold =
- Value::LargestUInt(Value::minLargestInt) / 10;
- static const auto negative_last_digit =
- Value::LargestUInt(Value::minLargestInt) % 10;
-
- const auto threshold = isNegative ? negative_threshold : positive_threshold;
- const auto last_digit =
+ // We assume we can represent the largest and smallest integer types as
+ // unsigned integers with separate sign. This is only true if they can fit
+ // into an unsigned integer.
+ static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
+ "Int must be smaller than UInt");
+
+ // We need to convert minLargestInt into a positive number. The easiest way
+ // to do this conversion is to assume our "threshold" value of minLargestInt
+ // divided by 10 can fit in maxLargestInt when absolute valued. This should
+ // be a safe assumption.
+ static_assert(Value::minLargestInt <= -Value::maxLargestInt,
+ "The absolute value of minLargestInt must be greater than or "
+ "equal to maxLargestInt");
+ static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
+ "The absolute value of minLargestInt must be only 1 magnitude "
+ "larger than maxLargest Int");
+
+ static constexpr Value::LargestUInt positive_threshold =
+ Value::maxLargestUInt / 10;
+ static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
+
+ // For the negative values, we have to be more careful. Since typically
+ // -Value::minLargestInt will cause an overflow, we first divide by 10 and
+ // then take the inverse. This assumes that minLargestInt is only a single
+ // power of 10 different in magnitude, which we check above. For the last
+ // digit, we take the modulus before negating for the same reason.
+ static constexpr Value::LargestUInt negative_threshold =
+ Value::LargestUInt(-(Value::minLargestInt / 10));
+ static constexpr Value::UInt negative_last_digit =
+ Value::UInt(-(Value::minLargestInt % 10));
+
+ const Value::LargestUInt threshold =
+ isNegative ? negative_threshold : positive_threshold;
+ const Value::UInt max_last_digit =
isNegative ? negative_last_digit : positive_last_digit;
Value::LargestUInt value = 0;
if (c < '0' || c > '9')
return decodeDouble(token, decoded);
- const auto digit(static_cast<Value::UInt>(c - '0'));
+ const Value::UInt digit(static_cast<Value::UInt>(c - '0'));
if (value >= threshold) {
// We've hit or exceeded the max value divided by 10 (rounded down). If
// a) we've only just touched the limit, meaing value == threshold,
// b) this is the last digit, or
// c) it's small enough to fit in that rounding delta, we're okay.
// Otherwise treat this number as a double to avoid overflow.
- if (value > threshold || current != token.end_ || digit > last_digit) {
+ if (value > threshold || current != token.end_ ||
+ digit > max_last_digit) {
return decodeDouble(token, decoded);
}
}
value = value * 10 + digit;
}
- if (isNegative)
- decoded = -Value::LargestInt(value);
- else if (value <= Value::LargestUInt(Value::maxLargestInt))
+ if (isNegative) {
+ // We use the same magnitude assumption here, just in case.
+ const Value::UInt last_digit = static_cast<Value::UInt>(value % 10);
+ decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
+ } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
decoded = Value::LargestInt(value);
- else
+ } else {
decoded = value;
+ }
return true;
}
bool OurReader::decodeDouble(Token& token, Value& decoded) {
double value = 0;
- const int bufferSize = 32;
- int count;
- ptrdiff_t const length = token.end_ - token.start_;
-
- // Sanity check to avoid buffer overflow exploits.
- if (length < 0) {
- return addError("Unable to parse token length", token);
- }
- auto const ulength = static_cast<size_t>(length);
-
- // Avoid using a string constant for the format control string given to
- // sscanf, as this can cause hard to debug crashes on OS X. See here for more
- // info:
- //
- // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
- char format[] = "%lf";
-
- if (length <= bufferSize) {
- Char buffer[bufferSize + 1];
- memcpy(buffer, token.start_, ulength);
- buffer[length] = 0;
- fixNumericLocaleInput(buffer, buffer + length);
- count = sscanf(buffer, format, &value);
- } else {
- String buffer(token.start_, token.end_);
- count = sscanf(buffer.c_str(), format, &value);
- }
-
- if (count != 1)
+ const String buffer(token.start_, token.end_);
+ IStringStream is(buffer);
+ if (!(is >> value)) {
return addError(
"'" + String(token.start_, token.end_) + "' is not a number.", token);
+ }
decoded = value;
return true;
}
Location end = token.end_ - 1; // do not include '"'
while (current != end) {
Char c = *current++;
- if (c == '"')
+ if (c == '"') {
break;
- else if (c == '\\') {
+ } else if (c == '\\') {
if (current == end)
return addError("Empty escape sequence in string", token, current);
Char escape = *current++;
return true;
}
-bool OurReader::decodeUnicodeCodePoint(Token& token,
- Location& current,
- Location end,
- unsigned int& unicode) {
+bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
+ Location end, unsigned int& unicode) {
if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
return false;
return true;
}
-bool OurReader::decodeUnicodeEscapeSequence(Token& token,
- Location& current,
+bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
Location end,
unsigned int& ret_unicode) {
if (end - current < 4)
return false;
}
-bool OurReader::addErrorAndRecover(const String& message,
- Token& token,
+bool OurReader::addErrorAndRecover(const String& message, Token& token,
TokenType skipUntilToken) {
addError(message, token);
return recoverFromError(skipUntilToken);
return *current_++;
}
-void OurReader::getLocationLineAndColumn(Location location,
- int& line,
+void OurReader::getLocationLineAndColumn(Location location, int& line,
int& column) const {
Location current = begin_;
Location lastLineStart = current;
return allErrors;
}
-bool OurReader::pushError(const Value& value, const String& message) {
- ptrdiff_t length = end_ - begin_;
- if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
- return false;
- Token token;
- token.type_ = tokenError;
- token.start_ = begin_ + value.getOffsetStart();
- token.end_ = begin_ + value.getOffsetLimit();
- ErrorInfo info;
- info.token_ = token;
- info.message_ = message;
- info.extra_ = nullptr;
- errors_.push_back(info);
- return true;
-}
-
-bool OurReader::pushError(const Value& value,
- const String& message,
- const Value& extra) {
- ptrdiff_t length = end_ - begin_;
- if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
- extra.getOffsetLimit() > length)
- return false;
- Token token;
- token.type_ = tokenError;
- token.start_ = begin_ + value.getOffsetStart();
- token.end_ = begin_ + value.getOffsetLimit();
- ErrorInfo info;
- info.token_ = token;
- info.message_ = message;
- info.extra_ = begin_ + extra.getOffsetStart();
- errors_.push_back(info);
- return true;
-}
-
-bool OurReader::good() const { return errors_.empty(); }
-
class OurCharReader : public CharReader {
bool const collectComments_;
OurReader reader_;
public:
OurCharReader(bool collectComments, OurFeatures const& features)
: collectComments_(collectComments), reader_(features) {}
- bool parse(char const* beginDoc,
- char const* endDoc,
- Value* root,
+ bool parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) override {
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
if (errs) {
//////////////////////////////////
// global functions
-bool parseFromStream(CharReader::Factory const& fact,
- IStream& sin,
- Value* root,
+bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
String* errs) {
OStringStream ssin;
ssin << sin.rdbuf();
// Provide implementation equivalent of std::snprintf for older _MSC compilers
#if defined(_MSC_VER) && _MSC_VER < 1900
#include <stdarg.h>
-static int msvc_pre1900_c99_vsnprintf(char* outBuf,
- size_t size,
- const char* format,
- va_list ap) {
+static int msvc_pre1900_c99_vsnprintf(char* outBuf, size_t size,
+ const char* format, va_list ap) {
int count = -1;
if (size != 0)
count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
return count;
}
-int JSON_API msvc_pre1900_c99_snprintf(char* outBuf,
- size_t size,
- const char* format,
- ...) {
+int JSON_API msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
+ const char* format, ...) {
va_list ap;
va_start(ap, format);
const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap);
Value const& Value::nullRef = Value::nullSingleton();
#endif
-const Int Value::minInt = Int(~(UInt(-1) / 2));
-const Int Value::maxInt = Int(UInt(-1) / 2);
-const UInt Value::maxUInt = UInt(-1);
-#if defined(JSON_HAS_INT64)
-const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2));
-const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2);
-const UInt64 Value::maxUInt64 = UInt64(-1);
-// The constant is hard-coded because some compiler have trouble
-// converting Value::maxUInt64 to a double correctly (AIX/xlC).
-// Assumes that UInt64 is a 64 bits integer.
-static const double maxUInt64AsDouble = 18446744073709551615.0;
-#endif // defined(JSON_HAS_INT64)
-const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));
-const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
-const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
-
-const UInt Value::defaultRealPrecision = 17;
-
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
template <typename T, typename U>
static inline bool InRange(double d, T min, U max) {
// The casts can lose precision, but we are looking only for
// an approximate range. Might fail on edge cases though. ~cdunn
- // return d >= static_cast<double>(min) && d <= static_cast<double>(max);
- return d >= min && d <= max;
+ return d >= static_cast<double>(min) && d <= static_cast<double>(max);
}
#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
static inline double integerToDouble(Json::UInt64 value) {
0; // to avoid buffer over-run accidents by users later
return newString;
}
-inline static void decodePrefixedString(bool isPrefixed,
- char const* prefixed,
- unsigned* length,
- char const** value) {
+inline static void decodePrefixedString(bool isPrefixed, char const* prefixed,
+ unsigned* length, char const** value) {
if (!isPrefixed) {
*length = static_cast<unsigned>(strlen(prefixed));
*value = prefixed;
#if JSON_USE_EXCEPTION
Exception::Exception(String msg) : msg_(std::move(msg)) {}
-Exception::~Exception() JSONCPP_NOEXCEPT {}
+Exception::~Exception() JSONCPP_NOEXCEPT = default;
char const* Exception::what() const JSONCPP_NOEXCEPT { return msg_.c_str(); }
RuntimeError::RuntimeError(String const& msg) : Exception(msg) {}
LogicError::LogicError(String const& msg) : Exception(msg) {}
-[[noreturn]] void throwRuntimeError(String const& msg) {
+JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
throw RuntimeError(msg);
}
-[[noreturn]] void throwLogicError(String const& msg) {
+JSONCPP_NORETURN void throwLogicError(String const& msg) {
throw LogicError(msg);
}
#else // !JSON_USE_EXCEPTION
-[[noreturn]] void throwRuntimeError(String const& msg) { abort(); }
-[[noreturn]] void throwLogicError(String const& msg) { abort(); }
+JSONCPP_NORETURN void throwRuntimeError(String const& msg) { abort(); }
+JSONCPP_NORETURN void throwLogicError(String const& msg) { abort(); }
#endif
// //////////////////////////////////////////////////////////////////
Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {}
-Value::CZString::CZString(char const* str,
- unsigned length,
+Value::CZString::CZString(char const* str, unsigned length,
DuplicationPolicy allocate)
: cstr_(str) {
// allocate != duplicate
Value::CZString::~CZString() {
if (cstr_ && storage_.policy_ == duplicate) {
releaseStringValue(const_cast<char*>(cstr_),
- storage_.length_ + 1u); // +1 for null terminating
+ storage_.length_ + 1U); // +1 for null terminating
// character for sake of
// completeness but not actually
// necessary
bool Value::operator<(const Value& other) const {
int typeDelta = type() - other.type();
if (typeDelta)
- return typeDelta < 0 ? true : false;
+ return typeDelta < 0;
switch (type()) {
case nullValue:
return false;
return value_.bool_ < other.value_.bool_;
case stringValue: {
if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
- if (other.value_.string_)
- return true;
- else
- return false;
+ return other.value_.string_ != nullptr;
}
unsigned this_len;
unsigned other_len;
case nullValue:
return 0.0;
case booleanValue:
- return value_.bool_ ? 1.0f : 0.0f;
+ return value_.bool_ ? 1.0F : 0.0F;
default:
break;
}
case nullValue:
return false;
case intValue:
- return value_.int_ ? true : false;
+ return value_.int_ != 0;
case uintValue:
- return value_.uint_ ? true : false;
+ return value_.uint_ != 0;
case realValue: {
// According to JavaScript language zero or NaN is regarded as false
const auto value_classification = std::fpclassify(value_.real_);
switch (other) {
case nullValue:
return (isNumeric() && asDouble() == 0.0) ||
- (type() == booleanValue && value_.bool_ == false) ||
+ (type() == booleanValue && !value_.bool_) ||
(type() == stringValue && asString().empty()) ||
(type() == arrayValue && value_.map_->empty()) ||
(type() == objectValue && value_.map_->empty()) ||
bool Value::empty() const {
if (isNull() || isArray() || isObject())
- return size() == 0u;
+ return size() == 0U;
else
return false;
}
}
#endif
-Value& Value::append(const Value& value) { return (*this)[size()] = value; }
+Value& Value::append(const Value& value) { return append(Value(value)); }
Value& Value::append(Value&& value) {
- return (*this)[size()] = std::move(value);
+ JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
+ "in Json::Value::append: requires arrayValue");
+ if (type() == nullValue) {
+ *this = Value(arrayValue);
+ }
+ return this->value_.map_->emplace(size(), std::move(value)).first->second;
+}
+
+bool Value::insert(ArrayIndex index, Value newValue) {
+ JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
+ "in Json::Value::insert: requires arrayValue");
+ ArrayIndex length = size();
+ if (index > length) {
+ return false;
+ } else {
+ for (ArrayIndex i = length; i > index; i--) {
+ (*this)[i] = std::move((*this)[i - 1]);
+ }
+ (*this)[index] = std::move(newValue);
+ return true;
+ }
}
-Value Value::get(char const* begin,
- char const* end,
+Value Value::get(char const* begin, char const* end,
Value const& defaultValue) const {
Value const* found = find(begin, end);
return !found ? defaultValue : *found;
if (!ptr_) {
ptr_ = std::unique_ptr<Array>(new Array());
}
- (*ptr_)[slot] = std::move(comment);
+ // check comments array boundry.
+ if (slot < CommentPlacement::numberOfCommentPlacement) {
+ (*ptr_)[slot] = std::move(comment);
+ }
}
void Value::setComment(String comment, CommentPlacement placement) {
// class PathArgument
// //////////////////////////////////////////////////////////////////
-PathArgument::PathArgument() : key_() {}
+PathArgument::PathArgument() = default;
PathArgument::PathArgument(ArrayIndex index)
- : key_(), index_(index), kind_(kindIndex) {}
+ : index_(index), kind_(kindIndex) {}
-PathArgument::PathArgument(const char* key)
- : key_(key), index_(), kind_(kindKey) {}
+PathArgument::PathArgument(const char* key) : key_(key), kind_(kindKey) {}
-PathArgument::PathArgument(const String& key)
- : key_(key.c_str()), index_(), kind_(kindKey) {}
+PathArgument::PathArgument(String key) : key_(std::move(key)), kind_(kindKey) {}
// class Path
// //////////////////////////////////////////////////////////////////
-Path::Path(const String& path,
- const PathArgument& a1,
- const PathArgument& a2,
- const PathArgument& a3,
- const PathArgument& a4,
+Path::Path(const String& path, const PathArgument& a1, const PathArgument& a2,
+ const PathArgument& a3, const PathArgument& a4,
const PathArgument& a5) {
InArgs in;
in.reserve(5);
}
}
-void Path::addPathInArg(const String& /*path*/,
- const InArgs& in,
+void Path::addPathInArg(const String& /*path*/, const InArgs& in,
InArgs::const_iterator& itInArg,
PathArgument::Kind kind) {
if (itInArg == in.end()) {
const Value::ObjectValues::iterator& current)
: current_(current), isNull_(false) {}
-Value& ValueIteratorBase::deref() const { return current_->second; }
+Value& ValueIteratorBase::deref() { return current_->second; }
+const Value& ValueIteratorBase::deref() const { return current_->second; }
void ValueIteratorBase::increment() { ++current_; }
namespace Json {
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
-typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
+using StreamWriterPtr = std::unique_ptr<StreamWriter>;
#else
typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
#endif
#endif // # if defined(JSON_HAS_INT64)
namespace {
-String valueToString(double value,
- bool useSpecialFloats,
- unsigned int precision,
- PrecisionType precisionType) {
+String valueToString(double value, bool useSpecialFloats,
+ unsigned int precision, PrecisionType precisionType) {
// Print into the buffer. We need not request the alternative representation
// that always has a decimal point because JSON doesn't distinguish the
// concepts of reals and integers.
}
} // namespace
-String valueToString(double value,
- unsigned int precision,
+String valueToString(double value, unsigned int precision,
PrecisionType precisionType) {
return valueToString(value, false, precision, precisionType);
}
return result;
}
-static String valueToQuotedStringN(const char* value, unsigned length) {
+static String valueToQuotedStringN(const char* value, unsigned length,
+ bool emitUTF8 = false) {
if (value == nullptr)
return "";
// Should add a flag to allow this compatibility mode and prevent this
// sequence from occurring.
default: {
- unsigned int cp = utf8ToCodepoint(c, end);
- // don't escape non-control characters
- // (short escape sequence are applied above)
- if (cp < 0x80 && cp >= 0x20)
- result += static_cast<char>(cp);
- else if (cp < 0x10000) { // codepoint is in Basic Multilingual Plane
- result += "\\u";
- result += toHex16Bit(cp);
- } else { // codepoint is not in Basic Multilingual Plane
- // convert to surrogate pair first
- cp -= 0x10000;
- result += "\\u";
- result += toHex16Bit((cp >> 10) + 0xD800);
- result += "\\u";
- result += toHex16Bit((cp & 0x3FF) + 0xDC00);
+ if (emitUTF8) {
+ result += *c;
+ } else {
+ unsigned int codepoint = utf8ToCodepoint(c, end);
+ const unsigned int FIRST_NON_CONTROL_CODEPOINT = 0x20;
+ const unsigned int LAST_NON_CONTROL_CODEPOINT = 0x7F;
+ const unsigned int FIRST_SURROGATE_PAIR_CODEPOINT = 0x10000;
+ // don't escape non-control characters
+ // (short escape sequence are applied above)
+ if (FIRST_NON_CONTROL_CODEPOINT <= codepoint &&
+ codepoint <= LAST_NON_CONTROL_CODEPOINT) {
+ result += static_cast<char>(codepoint);
+ } else if (codepoint <
+ FIRST_SURROGATE_PAIR_CODEPOINT) { // codepoint is in Basic
+ // Multilingual Plane
+ result += "\\u";
+ result += toHex16Bit(codepoint);
+ } else { // codepoint is not in Basic Multilingual Plane
+ // convert to surrogate pair first
+ codepoint -= FIRST_SURROGATE_PAIR_CODEPOINT;
+ result += "\\u";
+ result += toHex16Bit((codepoint >> 10) + 0xD800);
+ result += "\\u";
+ result += toHex16Bit((codepoint & 0x3FF) + 0xDC00);
+ }
}
} break;
}
};
struct BuiltStyledStreamWriter : public StreamWriter {
- BuiltStyledStreamWriter(String indentation,
- CommentStyle::Enum cs,
- String colonSymbol,
- String nullSymbol,
- String endingLineFeedSymbol,
- bool useSpecialFloats,
- unsigned int precision,
+ BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs,
+ String colonSymbol, String nullSymbol,
+ String endingLineFeedSymbol, bool useSpecialFloats,
+ bool emitUTF8, unsigned int precision,
PrecisionType precisionType);
int write(Value const& root, OStream* sout) override;
void writeCommentAfterValueOnSameLine(Value const& root);
static bool hasCommentForValue(const Value& value);
- typedef std::vector<String> ChildValues;
+ using ChildValues = std::vector<String>;
ChildValues childValues_;
String indentString_;
bool addChildValues_ : 1;
bool indented_ : 1;
bool useSpecialFloats_ : 1;
+ bool emitUTF8_ : 1;
unsigned int precision_;
PrecisionType precisionType_;
};
-BuiltStyledStreamWriter::BuiltStyledStreamWriter(String indentation,
- CommentStyle::Enum cs,
- String colonSymbol,
- String nullSymbol,
- String endingLineFeedSymbol,
- bool useSpecialFloats,
- unsigned int precision,
- PrecisionType precisionType)
+BuiltStyledStreamWriter::BuiltStyledStreamWriter(
+ String indentation, CommentStyle::Enum cs, String colonSymbol,
+ String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats,
+ bool emitUTF8, unsigned int precision, PrecisionType precisionType)
: rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
addChildValues_(false), indented_(false),
- useSpecialFloats_(useSpecialFloats), precision_(precision),
- precisionType_(precisionType) {}
+ useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8),
+ precision_(precision), precisionType_(precisionType) {}
int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
sout_ = sout;
addChildValues_ = false;
char const* end;
bool ok = value.getString(&str, &end);
if (ok)
- pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
+ pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str),
+ emitUTF8_));
else
pushValue("");
break;
Value const& childValue = value[name];
writeCommentBeforeValue(childValue);
writeWithIndent(valueToQuotedStringN(
- name.data(), static_cast<unsigned>(name.length())));
+ name.data(), static_cast<unsigned>(name.length()), emitUTF8_));
*sout_ << colonSymbol_;
writeValue(childValue);
if (++it == members.end()) {
StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }
StreamWriterBuilder::~StreamWriterBuilder() = default;
StreamWriter* StreamWriterBuilder::newStreamWriter() const {
- String indentation = settings_["indentation"].asString();
- String cs_str = settings_["commentStyle"].asString();
- String pt_str = settings_["precisionType"].asString();
- bool eyc = settings_["enableYAMLCompatibility"].asBool();
- bool dnp = settings_["dropNullPlaceholders"].asBool();
- bool usf = settings_["useSpecialFloats"].asBool();
+ const String indentation = settings_["indentation"].asString();
+ const String cs_str = settings_["commentStyle"].asString();
+ const String pt_str = settings_["precisionType"].asString();
+ const bool eyc = settings_["enableYAMLCompatibility"].asBool();
+ const bool dnp = settings_["dropNullPlaceholders"].asBool();
+ const bool usf = settings_["useSpecialFloats"].asBool();
+ const bool emitUTF8 = settings_["emitUTF8"].asBool();
unsigned int pre = settings_["precision"].asUInt();
CommentStyle::Enum cs = CommentStyle::All;
if (cs_str == "All") {
pre = 17;
String endingLineFeedSymbol;
return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
- endingLineFeedSymbol, usf, pre,
+ endingLineFeedSymbol, usf, emitUTF8, pre,
precisionType);
}
static void getValidWriterKeys(std::set<String>* valid_keys) {
valid_keys->insert("enableYAMLCompatibility");
valid_keys->insert("dropNullPlaceholders");
valid_keys->insert("useSpecialFloats");
+ valid_keys->insert("emitUTF8");
valid_keys->insert("precision");
valid_keys->insert("precisionType");
}
(*settings)["enableYAMLCompatibility"] = false;
(*settings)["dropNullPlaceholders"] = false;
(*settings)["useSpecialFloats"] = false;
+ (*settings)["emitUTF8"] = false;
(*settings)["precision"] = 17;
(*settings)["precisionType"] = "significant";
//! [StreamWriterBuilderDefaults]
+++ /dev/null
-// DO NOT EDIT. This file (and "version") is a template used by the build system
-// (either CMake or Meson) to generate a "version.h" header file.
-#ifndef JSON_VERSION_H_INCLUDED
-#define JSON_VERSION_H_INCLUDED
-
-#define JSONCPP_VERSION_STRING "@JSONCPP_VERSION@"
-#define JSONCPP_VERSION_MAJOR @JSONCPP_VERSION_MAJOR@
-#define JSONCPP_VERSION_MINOR @JSONCPP_VERSION_MINOR@
-#define JSONCPP_VERSION_PATCH @JSONCPP_VERSION_PATCH@
-#define JSONCPP_VERSION_QUALIFIER
-#define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) \
- | (JSONCPP_VERSION_MINOR << 16) \
- | (JSONCPP_VERSION_PATCH << 8))
-
-#ifdef JSONCPP_USING_SECURE_MEMORY
-#undef JSONCPP_USING_SECURE_MEMORY
-#endif
-#define JSONCPP_USING_SECURE_MEMORY @JSONCPP_USE_SECURE_MEMORY@
-// If non-zero, the library zeroes any memory that it has allocated before
-// it frees its memory.
-
-#endif // JSON_VERSION_H_INCLUDED
if(BUILD_SHARED_LIBS)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
add_compile_definitions( JSON_DLL )
+ else()
+ add_definitions( -DJSON_DLL )
+ endif()
endif()
target_link_libraries(jsoncpp_test jsoncpp_lib)
return 0;
}
- uint32_t hash_settings = *(const uint32_t*)data;
+ const uint32_t hash_settings = static_cast<uint32_t>(data[0]) |
+ (static_cast<uint32_t>(data[1]) << 8) |
+ (static_cast<uint32_t>(data[2]) << 16) |
+ (static_cast<uint32_t>(data[3]) << 24);
data += sizeof(uint32_t);
+ size -= sizeof(uint32_t);
builder.settings_["failIfExtra"] = hash_settings & (1 << 0);
builder.settings_["allowComments_"] = hash_settings & (1 << 1);
builder.settings_["failIfExtra_"] = hash_settings & (1 << 6);
builder.settings_["rejectDupKeys_"] = hash_settings & (1 << 7);
builder.settings_["allowSpecialFloats_"] = hash_settings & (1 << 8);
+ builder.settings_["collectComments"] = hash_settings & (1 << 9);
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
--- /dev/null
+#
+# AFL dictionary for JSON
+# -----------------------
+#
+# Just the very basics.
+#
+# Inspired by a dictionary by Jakub Wilk <jwilk@jwilk.net>
+#
+# https://github.com/rc0r/afl-fuzz/blob/master/dictionaries/json.dict
+#
+
+"0"
+",0"
+":0"
+"0:"
+"-1.2e+3"
+
+"true"
+"false"
+"null"
+
+"\"\""
+",\"\""
+":\"\""
+"\"\":"
+
+"{}"
+",{}"
+":{}"
+"{\"\":0}"
+"{{}}"
+
+"[]"
+",[]"
+":[]"
+"[0]"
+"[[]]"
+
+"''"
+"\\"
+"\\b"
+"\\f"
+"\\n"
+"\\r"
+"\\t"
+"\\u0000"
+"\\x00"
+"\\0"
+"\\uD800\\uDC00"
+"\\uDBFF\\uDFFF"
+
+"\"\":0"
+"//"
+"/**/"
void TestResult::setTestName(const Json::String& name) { name_ = name; }
-TestResult&
-TestResult::addFailure(const char* file, unsigned int line, const char* expr) {
+TestResult& TestResult::addFailure(const char* file, unsigned int line,
+ const char* expr) {
/// Walks the PredicateContext stack adding them to failures_ if not already
/// added.
unsigned int nestingLevel = 0;
return *this;
}
-void TestResult::addFailureInfo(const char* file,
- unsigned int line,
- const char* expr,
- unsigned int nestingLevel) {
+void TestResult::addFailureInfo(const char* file, unsigned int line,
+ const char* expr, unsigned int nestingLevel) {
Failure failure;
failure.file_ = file;
failure.line_ = line;
#if defined(_MSC_VER) && defined(_DEBUG)
// Hook MSVCRT assertions to prevent dialog from appearing
-static int
-msvcrtSilentReportHook(int reportType, char* message, int* /*returnValue*/) {
+static int msvcrtSilentReportHook(int reportType, char* message,
+ int* /*returnValue*/) {
// The default CRT handling of error and assertion is to display
// an error dialog to the user.
// Instead, when an error or an assertion occurs, we force the
}
#endif
-TestResult& checkStringEqual(TestResult& result,
- const Json::String& expected,
- const Json::String& actual,
- const char* file,
- unsigned int line,
- const char* expr) {
+TestResult& checkStringEqual(TestResult& result, const Json::String& expected,
+ const Json::String& actual, const char* file,
+ unsigned int line, const char* expr) {
if (expected != actual) {
result.addFailure(file, line, expr);
result << "Expected: '" << expected << "'\n";
#include <cstdio>
#include <deque>
+#include <iomanip>
#include <json/config.h>
#include <json/value.h>
#include <json/writer.h>
void setTestName(const Json::String& name);
/// Adds an assertion failure.
- TestResult&
- addFailure(const char* file, unsigned int line, const char* expr = nullptr);
+ TestResult& addFailure(const char* file, unsigned int line,
+ const char* expr = nullptr);
/// Removes the last PredicateContext added to the predicate stack
/// chained list.
// Generic operator that will work with anything ostream can deal with.
template <typename T> TestResult& operator<<(const T& value) {
Json::OStringStream oss;
- oss.precision(16);
- oss.setf(std::ios_base::floatfield);
- oss << value;
+ oss << std::setprecision(16) << std::hexfloat << value;
return addToLastFailure(oss.str());
}
private:
TestResult& addToLastFailure(const Json::String& message);
/// Adds a failure or a predicate context
- void addFailureInfo(const char* file,
- unsigned int line,
- const char* expr,
+ void addFailureInfo(const char* file, unsigned int line, const char* expr,
unsigned int nestingLevel);
static Json::String indentText(const Json::String& text,
const Json::String& indent);
};
template <typename T, typename U>
-TestResult& checkEqual(TestResult& result,
- T expected,
- U actual,
- const char* file,
- unsigned int line,
- const char* expr) {
+TestResult& checkEqual(TestResult& result, T expected, U actual,
+ const char* file, unsigned int line, const char* expr) {
if (static_cast<U>(expected) != actual) {
result.addFailure(file, line, expr);
result << "Expected: " << static_cast<U>(expected) << "\n";
Json::String ToJsonString(std::string in);
#endif
-TestResult& checkStringEqual(TestResult& result,
- const Json::String& expected,
- const Json::String& actual,
- const char* file,
- unsigned int line,
- const char* expr);
+TestResult& checkStringEqual(TestResult& result, const Json::String& expected,
+ const Json::String& actual, const char* file,
+ unsigned int line, const char* expr);
} // namespace JsonTest
#define JSONTEST_REGISTER_FIXTURE(runner, FixtureType, name) \
(runner).add(JSONTEST_FIXTURE_FACTORY(FixtureType, name))
+/// \brief Begin a fixture test case.
+#define JSONTEST_FIXTURE_V2(FixtureType, name, collections) \
+ class Test##FixtureType##name : public FixtureType { \
+ public: \
+ static JsonTest::TestCase* factory() { \
+ return new Test##FixtureType##name(); \
+ } \
+ static bool collect() { \
+ (collections).push_back(JSONTEST_FIXTURE_FACTORY(FixtureType, name)); \
+ return true; \
+ } \
+ \
+ public: /* overridden from TestCase */ \
+ const char* testName() const override { return #FixtureType "/" #name; } \
+ void runTestCase() override; \
+ }; \
+ \
+ static bool test##FixtureType##name##collect = \
+ Test##FixtureType##name::collect(); \
+ \
+ void Test##FixtureType##name::runTestCase()
+
#endif // ifndef JSONTEST_H_INCLUDED
#include <cstring>
#include <iomanip>
#include <iostream>
+#include <iterator>
#include <json/config.h>
#include <json/json.h>
#include <limits>
}
#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+// local_ is the collection for the testcases in this code file.
+static std::deque<JsonTest::TestCaseFactory> local_;
+#define JSONTEST_FIXTURE_LOCAL(FixtureType, name) \
+ JSONTEST_FIXTURE_V2(FixtureType, name, local_)
+
struct ValueTest : JsonTest::TestCase {
Json::Value null_;
Json::Value emptyArray_;
return s;
}
-JSONTEST_FIXTURE(ValueTest, checkNormalizeFloatingPointStr) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, checkNormalizeFloatingPointStr) {
JSONTEST_ASSERT_STRING_EQUAL("0.0", normalizeFloatingPointStr("0.0"));
JSONTEST_ASSERT_STRING_EQUAL("0e0", normalizeFloatingPointStr("0e0"));
JSONTEST_ASSERT_STRING_EQUAL("1234.0", normalizeFloatingPointStr("1234.0"));
JSONTEST_ASSERT_STRING_EQUAL("1234.0e0",
normalizeFloatingPointStr("1234.0e0"));
+ JSONTEST_ASSERT_STRING_EQUAL("1234.0e-1",
+ normalizeFloatingPointStr("1234.0e-1"));
JSONTEST_ASSERT_STRING_EQUAL("1234.0e+0",
normalizeFloatingPointStr("1234.0e+0"));
+ JSONTEST_ASSERT_STRING_EQUAL("1234.0e+1",
+ normalizeFloatingPointStr("1234.0e+001"));
JSONTEST_ASSERT_STRING_EQUAL("1234e-1", normalizeFloatingPointStr("1234e-1"));
+ JSONTEST_ASSERT_STRING_EQUAL("1234e+0",
+ normalizeFloatingPointStr("1234e+000"));
+ JSONTEST_ASSERT_STRING_EQUAL("1234e+1",
+ normalizeFloatingPointStr("1234e+001"));
JSONTEST_ASSERT_STRING_EQUAL("1234e10", normalizeFloatingPointStr("1234e10"));
JSONTEST_ASSERT_STRING_EQUAL("1234e10",
normalizeFloatingPointStr("1234e010"));
normalizeFloatingPointStr("1234e+100"));
JSONTEST_ASSERT_STRING_EQUAL("1234e-100",
normalizeFloatingPointStr("1234e-100"));
- JSONTEST_ASSERT_STRING_EQUAL("1234e+1",
- normalizeFloatingPointStr("1234e+001"));
}
-JSONTEST_FIXTURE(ValueTest, memberCount) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, memberCount) {
JSONTEST_ASSERT_PRED(checkMemberCount(emptyArray_, 0));
JSONTEST_ASSERT_PRED(checkMemberCount(emptyObject_, 0));
JSONTEST_ASSERT_PRED(checkMemberCount(array1_, 1));
JSONTEST_ASSERT_PRED(checkMemberCount(emptyString_, 0));
JSONTEST_ASSERT_PRED(checkMemberCount(string_, 0));
JSONTEST_ASSERT_PRED(checkMemberCount(true_, 0));
+ JSONTEST_ASSERT_PRED(checkMemberCount(false_, 0));
+ JSONTEST_ASSERT_PRED(checkMemberCount(string1_, 0));
+ JSONTEST_ASSERT_PRED(checkMemberCount(float_, 0));
}
-JSONTEST_FIXTURE(ValueTest, objects) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, objects) {
// Types
IsCheck checks;
checks.isObject_ = true;
did = object1_.removeMember("some other id", gotPtr);
JSONTEST_ASSERT_EQUAL(nullptr, gotPtr);
JSONTEST_ASSERT_EQUAL(true, did);
+
+ // Using other removeMember interfaces, the test idea is the same as above.
+ object1_["some other id"] = "foo";
+ const Json::String key("some other id");
+ did = object1_.removeMember(key, &got);
+ JSONTEST_ASSERT_EQUAL(Json::Value("foo"), got);
+ JSONTEST_ASSERT_EQUAL(true, did);
+ got = Json::Value("bar");
+ did = object1_.removeMember(key, &got);
+ JSONTEST_ASSERT_EQUAL(Json::Value("bar"), got);
+ JSONTEST_ASSERT_EQUAL(false, did);
+
+ object1_["some other id"] = "foo";
+ object1_.removeMember(key);
+ JSONTEST_ASSERT_EQUAL(Json::nullValue, object1_[key]);
}
-JSONTEST_FIXTURE(ValueTest, arrays) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, arrays) {
const unsigned int index0 = 0;
// Types
JSONTEST_ASSERT_EQUAL(Json::Value(17), got);
JSONTEST_ASSERT_EQUAL(false, array1_.removeIndex(2, &got)); // gone now
}
-JSONTEST_FIXTURE(ValueTest, arrayIssue252) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, resizeArray) {
+ Json::Value array;
+ {
+ for (Json::ArrayIndex i = 0; i < 10; i++)
+ array[i] = i;
+ JSONTEST_ASSERT_EQUAL(array.size(), 10);
+ // The length set is greater than the length of the array.
+ array.resize(15);
+ JSONTEST_ASSERT_EQUAL(array.size(), 15);
+
+ // The length set is less than the length of the array.
+ array.resize(5);
+ JSONTEST_ASSERT_EQUAL(array.size(), 5);
+
+ // The length of the array is set to 0.
+ array.resize(0);
+ JSONTEST_ASSERT_EQUAL(array.size(), 0);
+ }
+ {
+ for (Json::ArrayIndex i = 0; i < 10; i++)
+ array[i] = i;
+ JSONTEST_ASSERT_EQUAL(array.size(), 10);
+ array.clear();
+ JSONTEST_ASSERT_EQUAL(array.size(), 0);
+ }
+}
+JSONTEST_FIXTURE_LOCAL(ValueTest, getArrayValue) {
+ Json::Value array;
+ for (Json::ArrayIndex i = 0; i < 5; i++)
+ array[i] = i;
+
+ JSONTEST_ASSERT_EQUAL(array.size(), 5);
+ const Json::Value defaultValue(10);
+ Json::ArrayIndex index = 0;
+ for (; index <= 4; index++)
+ JSONTEST_ASSERT_EQUAL(index, array.get(index, defaultValue).asInt());
+
+ index = 4;
+ JSONTEST_ASSERT_EQUAL(array.isValidIndex(index), true);
+ index = 5;
+ JSONTEST_ASSERT_EQUAL(array.isValidIndex(index), false);
+ JSONTEST_ASSERT_EQUAL(defaultValue, array.get(index, defaultValue));
+ JSONTEST_ASSERT_EQUAL(array.isValidIndex(index), false);
+}
+JSONTEST_FIXTURE_LOCAL(ValueTest, arrayIssue252) {
int count = 5;
Json::Value root;
Json::Value item;
- root["array"] = Json::Value::nullRef;
+ root["array"] = Json::Value::nullSingleton();
for (int i = 0; i < count; i++) {
item["a"] = i;
item["b"] = i;
// JSONTEST_ASSERT_EQUAL(5, root["array"].size());
}
-JSONTEST_FIXTURE(ValueTest, null) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, arrayInsertAtRandomIndex) {
+ Json::Value array;
+ const Json::Value str0("index2");
+ const Json::Value str1("index3");
+ array.append("index0"); // append rvalue
+ array.append("index1");
+ array.append(str0); // append lvalue
+
+ std::vector<Json::Value*> vec; // storage value address for checking
+ for (Json::ArrayIndex i = 0; i < 3; i++) {
+ vec.push_back(&array[i]);
+ }
+ JSONTEST_ASSERT_EQUAL(Json::Value("index0"), array[0]); // check append
+ JSONTEST_ASSERT_EQUAL(Json::Value("index1"), array[1]);
+ JSONTEST_ASSERT_EQUAL(Json::Value("index2"), array[2]);
+
+ // insert lvalue at the head
+ JSONTEST_ASSERT(array.insert(0, str1));
+ JSONTEST_ASSERT_EQUAL(Json::Value("index3"), array[0]);
+ JSONTEST_ASSERT_EQUAL(Json::Value("index0"), array[1]);
+ JSONTEST_ASSERT_EQUAL(Json::Value("index1"), array[2]);
+ JSONTEST_ASSERT_EQUAL(Json::Value("index2"), array[3]);
+ // checking address
+ for (Json::ArrayIndex i = 0; i < 3; i++) {
+ JSONTEST_ASSERT_EQUAL(vec[i], &array[i]);
+ }
+ vec.push_back(&array[3]);
+ // insert rvalue at middle
+ JSONTEST_ASSERT(array.insert(2, "index4"));
+ JSONTEST_ASSERT_EQUAL(Json::Value("index3"), array[0]);
+ JSONTEST_ASSERT_EQUAL(Json::Value("index0"), array[1]);
+ JSONTEST_ASSERT_EQUAL(Json::Value("index4"), array[2]);
+ JSONTEST_ASSERT_EQUAL(Json::Value("index1"), array[3]);
+ JSONTEST_ASSERT_EQUAL(Json::Value("index2"), array[4]);
+ // checking address
+ for (Json::ArrayIndex i = 0; i < 4; i++) {
+ JSONTEST_ASSERT_EQUAL(vec[i], &array[i]);
+ }
+ vec.push_back(&array[4]);
+ // insert rvalue at the tail
+ Json::Value index5("index5");
+ JSONTEST_ASSERT(array.insert(5, std::move(index5)));
+ JSONTEST_ASSERT_EQUAL(Json::Value("index3"), array[0]);
+ JSONTEST_ASSERT_EQUAL(Json::Value("index0"), array[1]);
+ JSONTEST_ASSERT_EQUAL(Json::Value("index4"), array[2]);
+ JSONTEST_ASSERT_EQUAL(Json::Value("index1"), array[3]);
+ JSONTEST_ASSERT_EQUAL(Json::Value("index2"), array[4]);
+ JSONTEST_ASSERT_EQUAL(Json::Value("index5"), array[5]);
+ // checking address
+ for (Json::ArrayIndex i = 0; i < 5; i++) {
+ JSONTEST_ASSERT_EQUAL(vec[i], &array[i]);
+ }
+ vec.push_back(&array[5]);
+ // beyond max array size, it should not be allowed to insert into its tail
+ JSONTEST_ASSERT(!array.insert(10, "index10"));
+}
+
+JSONTEST_FIXTURE_LOCAL(ValueTest, null) {
JSONTEST_ASSERT_EQUAL(Json::nullValue, null_.type());
IsCheck checks;
JSONTEST_ASSERT_EQUAL(0.0, null_.asFloat());
JSONTEST_ASSERT_STRING_EQUAL("", null_.asString());
- JSONTEST_ASSERT_EQUAL(Json::Value::null, null_);
+ JSONTEST_ASSERT_EQUAL(Json::Value::nullSingleton(), null_);
// Test using a Value in a boolean context (false iff null)
JSONTEST_ASSERT_EQUAL(null_, false);
JSONTEST_ASSERT_EQUAL(!object1_, false);
}
-JSONTEST_FIXTURE(ValueTest, strings) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, strings) {
JSONTEST_ASSERT_EQUAL(Json::stringValue, string1_.type());
IsCheck checks;
JSONTEST_ASSERT_STRING_EQUAL("a", string1_.asCString());
}
-JSONTEST_FIXTURE(ValueTest, bools) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, bools) {
JSONTEST_ASSERT_EQUAL(Json::booleanValue, false_.type());
IsCheck checks;
JSONTEST_ASSERT_EQUAL(0.0, false_.asFloat());
}
-JSONTEST_FIXTURE(ValueTest, integers) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, integers) {
IsCheck checks;
Json::Value val;
#endif
}
-JSONTEST_FIXTURE(ValueTest, nonIntegers) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, nonIntegers) {
IsCheck checks;
Json::Value val;
#endif
}
-JSONTEST_FIXTURE(ValueTest, compareNull) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, compareNull) {
JSONTEST_ASSERT_PRED(checkIsEqual(Json::Value(), Json::Value()));
+ JSONTEST_ASSERT_PRED(
+ checkIsEqual(Json::Value::nullSingleton(), Json::Value()));
+ JSONTEST_ASSERT_PRED(
+ checkIsEqual(Json::Value::nullSingleton(), Json::Value::nullSingleton()));
}
-JSONTEST_FIXTURE(ValueTest, compareInt) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, compareInt) {
JSONTEST_ASSERT_PRED(checkIsLess(0, 10));
JSONTEST_ASSERT_PRED(checkIsEqual(10, 10));
JSONTEST_ASSERT_PRED(checkIsEqual(-10, -10));
JSONTEST_ASSERT_PRED(checkIsLess(-10, 0));
}
-JSONTEST_FIXTURE(ValueTest, compareUInt) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, compareUInt) {
JSONTEST_ASSERT_PRED(checkIsLess(0u, 10u));
JSONTEST_ASSERT_PRED(checkIsLess(0u, Json::Value::maxUInt));
JSONTEST_ASSERT_PRED(checkIsEqual(10u, 10u));
}
-JSONTEST_FIXTURE(ValueTest, compareDouble) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, compareDouble) {
JSONTEST_ASSERT_PRED(checkIsLess(0.0, 10.0));
JSONTEST_ASSERT_PRED(checkIsEqual(10.0, 10.0));
JSONTEST_ASSERT_PRED(checkIsEqual(-10.0, -10.0));
JSONTEST_ASSERT_PRED(checkIsLess(-10.0, 0.0));
}
-JSONTEST_FIXTURE(ValueTest, compareString) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, compareString) {
JSONTEST_ASSERT_PRED(checkIsLess("", " "));
JSONTEST_ASSERT_PRED(checkIsLess("", "a"));
JSONTEST_ASSERT_PRED(checkIsLess("abcd", "zyui"));
JSONTEST_ASSERT_PRED(checkIsEqual("ABCD", "ABCD"));
}
-JSONTEST_FIXTURE(ValueTest, compareBoolean) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, compareBoolean) {
JSONTEST_ASSERT_PRED(checkIsLess(false, true));
JSONTEST_ASSERT_PRED(checkIsEqual(false, false));
JSONTEST_ASSERT_PRED(checkIsEqual(true, true));
}
-JSONTEST_FIXTURE(ValueTest, compareArray) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, compareArray) {
// array compare size then content
Json::Value emptyArray(Json::arrayValue);
Json::Value l1aArray;
l2bArray.append(10);
JSONTEST_ASSERT_PRED(checkIsLess(emptyArray, l1aArray));
JSONTEST_ASSERT_PRED(checkIsLess(emptyArray, l2aArray));
- JSONTEST_ASSERT_PRED(checkIsLess(l1aArray, l2aArray));
+ JSONTEST_ASSERT_PRED(checkIsLess(l1aArray, l1bArray));
+ JSONTEST_ASSERT_PRED(checkIsLess(l1bArray, l2aArray));
JSONTEST_ASSERT_PRED(checkIsLess(l2aArray, l2bArray));
JSONTEST_ASSERT_PRED(checkIsEqual(emptyArray, Json::Value(emptyArray)));
JSONTEST_ASSERT_PRED(checkIsEqual(l1aArray, Json::Value(l1aArray)));
+ JSONTEST_ASSERT_PRED(checkIsEqual(l1bArray, Json::Value(l1bArray)));
+ JSONTEST_ASSERT_PRED(checkIsEqual(l2aArray, Json::Value(l2aArray)));
JSONTEST_ASSERT_PRED(checkIsEqual(l2bArray, Json::Value(l2bArray)));
}
-JSONTEST_FIXTURE(ValueTest, compareObject) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, compareObject) {
// object compare size then content
Json::Value emptyObject(Json::objectValue);
Json::Value l1aObject;
l1aObject["key1"] = 0;
Json::Value l1bObject;
- l1aObject["key1"] = 10;
+ l1bObject["key1"] = 10;
Json::Value l2aObject;
l2aObject["key1"] = 0;
l2aObject["key2"] = 0;
+ Json::Value l2bObject;
+ l2bObject["key1"] = 10;
+ l2bObject["key2"] = 0;
JSONTEST_ASSERT_PRED(checkIsLess(emptyObject, l1aObject));
- JSONTEST_ASSERT_PRED(checkIsLess(emptyObject, l2aObject));
- JSONTEST_ASSERT_PRED(checkIsLess(l1aObject, l2aObject));
+ JSONTEST_ASSERT_PRED(checkIsLess(l1aObject, l1bObject));
+ JSONTEST_ASSERT_PRED(checkIsLess(l1bObject, l2aObject));
+ JSONTEST_ASSERT_PRED(checkIsLess(l2aObject, l2bObject));
JSONTEST_ASSERT_PRED(checkIsEqual(emptyObject, Json::Value(emptyObject)));
JSONTEST_ASSERT_PRED(checkIsEqual(l1aObject, Json::Value(l1aObject)));
+ JSONTEST_ASSERT_PRED(checkIsEqual(l1bObject, Json::Value(l1bObject)));
JSONTEST_ASSERT_PRED(checkIsEqual(l2aObject, Json::Value(l2aObject)));
+ JSONTEST_ASSERT_PRED(checkIsEqual(l2bObject, Json::Value(l2bObject)));
+ {
+ Json::Value aObject;
+ aObject["a"] = 10;
+ Json::Value bObject;
+ bObject["b"] = 0;
+ Json::Value cObject;
+ cObject["c"] = 20;
+ cObject["f"] = 15;
+ Json::Value dObject;
+ dObject["d"] = -2;
+ dObject["e"] = 10;
+ JSONTEST_ASSERT_PRED(checkIsLess(aObject, bObject));
+ JSONTEST_ASSERT_PRED(checkIsLess(bObject, cObject));
+ JSONTEST_ASSERT_PRED(checkIsLess(cObject, dObject));
+ JSONTEST_ASSERT_PRED(checkIsEqual(aObject, Json::Value(aObject)));
+ JSONTEST_ASSERT_PRED(checkIsEqual(bObject, Json::Value(bObject)));
+ JSONTEST_ASSERT_PRED(checkIsEqual(cObject, Json::Value(cObject)));
+ JSONTEST_ASSERT_PRED(checkIsEqual(dObject, Json::Value(dObject)));
+ }
}
-JSONTEST_FIXTURE(ValueTest, compareType) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, compareType) {
// object of different type are ordered according to their type
JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(), Json::Value(1)));
JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(1), Json::Value(1u)));
Json::Value(Json::objectValue)));
}
-JSONTEST_FIXTURE(ValueTest, CopyObject) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, CopyObject) {
Json::Value arrayVal;
arrayVal.append("val1");
arrayVal.append("val2");
JSONTEST_ASSERT(copy1 == "string value");
copy2.copy(arrayVal);
JSONTEST_ASSERT(copy2.size() == 5);
+ {
+ Json::Value srcObject, objectCopy, otherObject;
+ srcObject["key0"] = 10;
+ objectCopy.copy(srcObject);
+ JSONTEST_ASSERT(srcObject["key0"] == 10);
+ JSONTEST_ASSERT(objectCopy["key0"] == 10);
+ JSONTEST_ASSERT(srcObject.getMemberNames().size() == 1);
+ JSONTEST_ASSERT(objectCopy.getMemberNames().size() == 1);
+ otherObject["key1"] = 15;
+ otherObject["key2"] = 16;
+ JSONTEST_ASSERT(otherObject.getMemberNames().size() == 2);
+ objectCopy.copy(otherObject);
+ JSONTEST_ASSERT(objectCopy["key1"] == 15);
+ JSONTEST_ASSERT(objectCopy["key2"] == 16);
+ JSONTEST_ASSERT(objectCopy.getMemberNames().size() == 2);
+ otherObject["key1"] = 20;
+ JSONTEST_ASSERT(objectCopy["key1"] == 15);
+ }
}
void ValueTest::checkIsLess(const Json::Value& x, const Json::Value& y) {
JSONTEST_ASSERT(y.compare(x) == 0);
}
-JSONTEST_FIXTURE(ValueTest, typeChecksThrowExceptions) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, typeChecksThrowExceptions) {
#if JSON_USE_EXCEPTION
Json::Value intVal(1);
#endif
}
-JSONTEST_FIXTURE(ValueTest, offsetAccessors) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, offsetAccessors) {
Json::Value x;
JSONTEST_ASSERT(x.getOffsetStart() == 0);
JSONTEST_ASSERT(x.getOffsetLimit() == 0);
JSONTEST_ASSERT(y.getOffsetLimit() == 0);
}
-JSONTEST_FIXTURE(ValueTest, StaticString) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, StaticString) {
char mutant[] = "hello";
Json::StaticString ss(mutant);
Json::String regular(mutant);
}
}
-JSONTEST_FIXTURE(ValueTest, WideString) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, WideString) {
// https://github.com/open-source-parsers/jsoncpp/issues/756
const std::string uni = u8"式,进"; // "\u5f0f\uff0c\u8fdb"
std::string styled;
JSONTEST_ASSERT_STRING_EQUAL(root["abc"].asString(), uni);
}
-JSONTEST_FIXTURE(ValueTest, CommentBefore) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, CommentBefore) {
Json::Value val; // fill val
val.setComment(Json::String("// this comment should appear before"),
Json::commentBefore);
}
}
-JSONTEST_FIXTURE(ValueTest, zeroes) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, zeroes) {
char const cstr[] = "h\0i";
Json::String binary(cstr, sizeof(cstr)); // include trailing 0
JSONTEST_ASSERT_EQUAL(4U, binary.length());
}
}
-JSONTEST_FIXTURE(ValueTest, zeroesInKeys) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, zeroesInKeys) {
char const cstr[] = "h\0i";
Json::String binary(cstr, sizeof(cstr)); // include trailing 0
JSONTEST_ASSERT_EQUAL(4U, binary.length());
JSONTEST_ASSERT(!root.isMember("h"));
JSONTEST_ASSERT(root.isMember(binary));
JSONTEST_ASSERT_STRING_EQUAL(
- "there", root.get(binary, Json::Value::nullRef).asString());
+ "there", root.get(binary, Json::Value::nullSingleton()).asString());
Json::Value removed;
bool did;
did = root.removeMember(binary.data(), binary.data() + binary.length(),
JSONTEST_ASSERT_STRING_EQUAL("there", removed.asString()); // still
JSONTEST_ASSERT(!root.isMember(binary));
JSONTEST_ASSERT_STRING_EQUAL(
- "", root.get(binary, Json::Value::nullRef).asString());
+ "", root.get(binary, Json::Value::nullSingleton()).asString());
}
}
-JSONTEST_FIXTURE(ValueTest, specialFloats) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, specialFloats) {
Json::StreamWriterBuilder b;
b.settings_["useSpecialFloats"] = true;
JSONTEST_ASSERT_STRING_EQUAL(expected, result);
}
-JSONTEST_FIXTURE(ValueTest, precision) {
+JSONTEST_FIXTURE_LOCAL(ValueTest, precision) {
Json::StreamWriterBuilder b;
b.settings_["precision"] = 5;
result = Json::writeString(b, v);
JSONTEST_ASSERT_STRING_EQUAL(expected, result);
}
+JSONTEST_FIXTURE_LOCAL(ValueTest, searchValueByPath) {
+ Json::Value root, subroot;
+ root["property1"][0] = 0;
+ root["property1"][1] = 1;
+ subroot["object"] = "object";
+ root["property2"] = subroot;
-struct WriterTest : JsonTest::TestCase {};
+ const Json::Value defaultValue("error");
+ Json::FastWriter writer;
+
+ {
+ const Json::String expected("{"
+ "\"property1\":[0,1],"
+ "\"property2\":{\"object\":\"object\"}"
+ "}\n");
+ Json::String outcome = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, outcome);
+
+ // Array member exists.
+ const Json::Path path1(".property1.[%]", 1);
+ Json::Value result = path1.resolve(root);
+ JSONTEST_ASSERT_EQUAL(Json::Value(1), result);
+ result = path1.resolve(root, defaultValue);
+ JSONTEST_ASSERT_EQUAL(Json::Value(1), result);
+
+ // Array member does not exist.
+ const Json::Path path2(".property1.[2]");
+ result = path2.resolve(root);
+ JSONTEST_ASSERT_EQUAL(Json::nullValue, result);
+ result = path2.resolve(root, defaultValue);
+ JSONTEST_ASSERT_EQUAL(defaultValue, result);
+
+ // Access array path form error
+ const Json::Path path3(".property1.0");
+ result = path3.resolve(root);
+ JSONTEST_ASSERT_EQUAL(Json::nullValue, result);
+ result = path3.resolve(root, defaultValue);
+ JSONTEST_ASSERT_EQUAL(defaultValue, result);
+
+ // Object member exists.
+ const Json::Path path4(".property2.%", "object");
+ result = path4.resolve(root);
+ JSONTEST_ASSERT_EQUAL(Json::Value("object"), result);
+ result = path4.resolve(root, defaultValue);
+ JSONTEST_ASSERT_EQUAL(Json::Value("object"), result);
+
+ // Object member does not exist.
+ const Json::Path path5(".property2.hello");
+ result = path5.resolve(root);
+ JSONTEST_ASSERT_EQUAL(Json::nullValue, result);
+ result = path5.resolve(root, defaultValue);
+ JSONTEST_ASSERT_EQUAL(defaultValue, result);
+
+ // Access object path form error
+ const Json::Path path6(".property2.[0]");
+ result = path5.resolve(root);
+ JSONTEST_ASSERT_EQUAL(Json::nullValue, result);
+ result = path6.resolve(root, defaultValue);
+ JSONTEST_ASSERT_EQUAL(defaultValue, result);
+
+ // resolve will not change the value
+ outcome = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, outcome);
+ }
+ {
+ const Json::String expected("{"
+ "\"property1\":[0,1,null],"
+ "\"property2\":{"
+ "\"hello\":null,"
+ "\"object\":\"object\"}}\n");
+ Json::Path path1(".property1.[%]", 2);
+ Json::Value& value1 = path1.make(root);
+ JSONTEST_ASSERT_EQUAL(Json::nullValue, value1);
+
+ Json::Path path2(".property2.%", "hello");
+ Json::Value& value2 = path2.make(root);
+ JSONTEST_ASSERT_EQUAL(Json::nullValue, value2);
+
+ // make will change the value
+ const Json::String outcome = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, outcome);
+ }
+}
+struct FastWriterTest : JsonTest::TestCase {};
-JSONTEST_FIXTURE(WriterTest, dropNullPlaceholders) {
+JSONTEST_FIXTURE_LOCAL(FastWriterTest, dropNullPlaceholders) {
Json::FastWriter writer;
Json::Value nullValue;
JSONTEST_ASSERT(writer.write(nullValue) == "null\n");
JSONTEST_ASSERT(writer.write(nullValue) == "\n");
}
+JSONTEST_FIXTURE_LOCAL(FastWriterTest, enableYAMLCompatibility) {
+ Json::FastWriter writer;
+ Json::Value root;
+ root["hello"] = "world";
+
+ JSONTEST_ASSERT(writer.write(root) == "{\"hello\":\"world\"}\n");
+
+ writer.enableYAMLCompatibility();
+ JSONTEST_ASSERT(writer.write(root) == "{\"hello\": \"world\"}\n");
+}
+
+JSONTEST_FIXTURE_LOCAL(FastWriterTest, omitEndingLineFeed) {
+ Json::FastWriter writer;
+ Json::Value nullValue;
+
+ JSONTEST_ASSERT(writer.write(nullValue) == "null\n");
+
+ writer.omitEndingLineFeed();
+ JSONTEST_ASSERT(writer.write(nullValue) == "null");
+}
+
+JSONTEST_FIXTURE_LOCAL(FastWriterTest, writeNumericValue) {
+ Json::FastWriter writer;
+ const Json::String expected("{"
+ "\"emptyValue\":null,"
+ "\"false\":false,"
+ "\"null\":\"null\","
+ "\"number\":-6200000000000000.0,"
+ "\"real\":1.256,"
+ "\"uintValue\":17"
+ "}\n");
+ Json::Value root;
+ root["emptyValue"] = Json::nullValue;
+ root["false"] = false;
+ root["null"] = "null";
+ root["number"] = -6.2e+15;
+ root["real"] = 1.256;
+ root["uintValue"] = Json::Value(17U);
+
+ const Json::String result = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+}
+
+JSONTEST_FIXTURE_LOCAL(FastWriterTest, writeArrays) {
+ Json::FastWriter writer;
+ const Json::String expected("{"
+ "\"property1\":[\"value1\",\"value2\"],"
+ "\"property2\":[]"
+ "}\n");
+ Json::Value root;
+ root["property1"][0] = "value1";
+ root["property1"][1] = "value2";
+ root["property2"] = Json::arrayValue;
+
+ const Json::String result = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+}
+
+JSONTEST_FIXTURE_LOCAL(FastWriterTest, writeNestedObjects) {
+ Json::FastWriter writer;
+ const Json::String expected("{"
+ "\"object1\":{"
+ "\"bool\":true,"
+ "\"nested\":123"
+ "},"
+ "\"object2\":{}"
+ "}\n");
+ Json::Value root, child;
+ child["nested"] = 123;
+ child["bool"] = true;
+ root["object1"] = child;
+ root["object2"] = Json::objectValue;
+
+ const Json::String result = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+}
+
+struct StyledWriterTest : JsonTest::TestCase {};
+
+JSONTEST_FIXTURE_LOCAL(StyledWriterTest, writeNumericValue) {
+ Json::StyledWriter writer;
+ const Json::String expected("{\n"
+ " \"emptyValue\" : null,\n"
+ " \"false\" : false,\n"
+ " \"null\" : \"null\",\n"
+ " \"number\" : -6200000000000000.0,\n"
+ " \"real\" : 1.256,\n"
+ " \"uintValue\" : 17\n"
+ "}\n");
+ Json::Value root;
+ root["emptyValue"] = Json::nullValue;
+ root["false"] = false;
+ root["null"] = "null";
+ root["number"] = -6.2e+15;
+ root["real"] = 1.256;
+ root["uintValue"] = Json::Value(17U);
+
+ const Json::String result = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+}
+
+JSONTEST_FIXTURE_LOCAL(StyledWriterTest, writeArrays) {
+ Json::StyledWriter writer;
+ const Json::String expected("{\n"
+ " \"property1\" : [ \"value1\", \"value2\" ],\n"
+ " \"property2\" : []\n"
+ "}\n");
+ Json::Value root;
+ root["property1"][0] = "value1";
+ root["property1"][1] = "value2";
+ root["property2"] = Json::arrayValue;
+
+ const Json::String result = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+}
+
+JSONTEST_FIXTURE_LOCAL(StyledWriterTest, writeNestedObjects) {
+ Json::StyledWriter writer;
+ const Json::String expected("{\n"
+ " \"object1\" : {\n"
+ " \"bool\" : true,\n"
+ " \"nested\" : 123\n"
+ " },\n"
+ " \"object2\" : {}\n"
+ "}\n");
+ Json::Value root, child;
+ child["nested"] = 123;
+ child["bool"] = true;
+ root["object1"] = child;
+ root["object2"] = Json::objectValue;
+
+ const Json::String result = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+}
+
+JSONTEST_FIXTURE_LOCAL(StyledWriterTest, multiLineArray) {
+ Json::StyledWriter writer;
+ {
+ // Array member has more than 20 print effect rendering lines
+ const Json::String expected("[\n "
+ "0,\n 1,\n 2,\n "
+ "3,\n 4,\n 5,\n "
+ "6,\n 7,\n 8,\n "
+ "9,\n 10,\n 11,\n "
+ "12,\n 13,\n 14,\n "
+ "15,\n 16,\n 17,\n "
+ "18,\n 19,\n 20\n]\n");
+ Json::Value root;
+ for (Json::ArrayIndex i = 0; i < 21; i++)
+ root[i] = i;
+ const Json::String result = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+ }
+ {
+ // Array members do not exceed 21 print effects to render a single line
+ const Json::String expected("[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]\n");
+ Json::Value root;
+ for (Json::ArrayIndex i = 0; i < 10; i++)
+ root[i] = i;
+ const Json::String result = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+ }
+}
+
+JSONTEST_FIXTURE_LOCAL(StyledWriterTest, writeValueWithComment) {
+ Json::StyledWriter writer;
+ {
+ const Json::String expected("\n//commentBeforeValue\n\"hello\"\n");
+ Json::Value root = "hello";
+ root.setComment(Json::String("//commentBeforeValue"), Json::commentBefore);
+ const Json::String result = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+ }
+ {
+ const Json::String expected("\"hello\" //commentAfterValueOnSameLine\n");
+ Json::Value root = "hello";
+ root.setComment(Json::String("//commentAfterValueOnSameLine"),
+ Json::commentAfterOnSameLine);
+ const Json::String result = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+ }
+ {
+ const Json::String expected("\"hello\"\n//commentAfter\n\n");
+ Json::Value root = "hello";
+ root.setComment(Json::String("//commentAfter"), Json::commentAfter);
+ const Json::String result = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+ }
+}
+
+struct StyledStreamWriterTest : JsonTest::TestCase {};
+
+JSONTEST_FIXTURE_LOCAL(StyledStreamWriterTest, writeNumericValue) {
+ Json::StyledStreamWriter writer;
+ const Json::String expected("{\n"
+ "\t\"emptyValue\" : null,\n"
+ "\t\"false\" : false,\n"
+ "\t\"null\" : \"null\",\n"
+ "\t\"number\" : -6200000000000000.0,\n"
+ "\t\"real\" : 1.256,\n"
+ "\t\"uintValue\" : 17\n"
+ "}\n");
+
+ Json::Value root;
+ root["emptyValue"] = Json::nullValue;
+ root["false"] = false;
+ root["null"] = "null";
+ root["number"] = -6.2e+15; // big float number
+ root["real"] = 1.256; // float number
+ root["uintValue"] = Json::Value(17U);
+
+ Json::OStringStream sout;
+ writer.write(sout, root);
+ const Json::String result = sout.str();
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+}
+
+JSONTEST_FIXTURE_LOCAL(StyledStreamWriterTest, writeArrays) {
+ Json::StyledStreamWriter writer;
+ const Json::String expected("{\n"
+ "\t\"property1\" : [ \"value1\", \"value2\" ],\n"
+ "\t\"property2\" : []\n"
+ "}\n");
+ Json::Value root;
+ root["property1"][0] = "value1";
+ root["property1"][1] = "value2";
+ root["property2"] = Json::arrayValue;
+
+ Json::OStringStream sout;
+ writer.write(sout, root);
+ const Json::String result = sout.str();
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+}
+
+JSONTEST_FIXTURE_LOCAL(StyledStreamWriterTest, writeNestedObjects) {
+ Json::StyledStreamWriter writer;
+ const Json::String expected("{\n"
+ "\t\"object1\" : \n"
+ "\t"
+ "{\n"
+ "\t\t\"bool\" : true,\n"
+ "\t\t\"nested\" : 123\n"
+ "\t},\n"
+ "\t\"object2\" : {}\n"
+ "}\n");
+ Json::Value root, child;
+ child["nested"] = 123;
+ child["bool"] = true;
+ root["object1"] = child;
+ root["object2"] = Json::objectValue;
+
+ Json::OStringStream sout;
+ writer.write(sout, root);
+ const Json::String result = sout.str();
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+}
+
+JSONTEST_FIXTURE_LOCAL(StyledStreamWriterTest, multiLineArray) {
+ {
+ // Array member has more than 20 print effect rendering lines
+ const Json::String expected("[\n\t0,"
+ "\n\t1,"
+ "\n\t2,"
+ "\n\t3,"
+ "\n\t4,"
+ "\n\t5,"
+ "\n\t6,"
+ "\n\t7,"
+ "\n\t8,"
+ "\n\t9,"
+ "\n\t10,"
+ "\n\t11,"
+ "\n\t12,"
+ "\n\t13,"
+ "\n\t14,"
+ "\n\t15,"
+ "\n\t16,"
+ "\n\t17,"
+ "\n\t18,"
+ "\n\t19,"
+ "\n\t20\n]\n");
+ Json::StyledStreamWriter writer;
+ Json::Value root;
+ for (Json::ArrayIndex i = 0; i < 21; i++)
+ root[i] = i;
+ Json::OStringStream sout;
+ writer.write(sout, root);
+ const Json::String result = sout.str();
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+ }
+ {
+ Json::StyledStreamWriter writer;
+ // Array members do not exceed 21 print effects to render a single line
+ const Json::String expected("[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]\n");
+ Json::Value root;
+ for (Json::ArrayIndex i = 0; i < 10; i++)
+ root[i] = i;
+ Json::OStringStream sout;
+ writer.write(sout, root);
+ const Json::String result = sout.str();
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+ }
+}
+
+JSONTEST_FIXTURE_LOCAL(StyledStreamWriterTest, writeValueWithComment) {
+ Json::StyledStreamWriter writer("\t");
+ {
+ const Json::String expected("//commentBeforeValue\n\"hello\"\n");
+ Json::Value root = "hello";
+ Json::OStringStream sout;
+ root.setComment(Json::String("//commentBeforeValue"), Json::commentBefore);
+ writer.write(sout, root);
+ const Json::String result = sout.str();
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+ }
+ {
+ const Json::String expected("\"hello\" //commentAfterValueOnSameLine\n");
+ Json::Value root = "hello";
+ Json::OStringStream sout;
+ root.setComment(Json::String("//commentAfterValueOnSameLine"),
+ Json::commentAfterOnSameLine);
+ writer.write(sout, root);
+ const Json::String result = sout.str();
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+ }
+ {
+ const Json::String expected("\"hello\"\n//commentAfter\n");
+ Json::Value root = "hello";
+ Json::OStringStream sout;
+ root.setComment(Json::String("//commentAfter"), Json::commentAfter);
+ writer.write(sout, root);
+ const Json::String result = sout.str();
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+ }
+}
+
struct StreamWriterTest : JsonTest::TestCase {};
-JSONTEST_FIXTURE(StreamWriterTest, dropNullPlaceholders) {
+JSONTEST_FIXTURE_LOCAL(StreamWriterTest, writeNumericValue) {
+ Json::StreamWriterBuilder writer;
+ const Json::String expected("{\n"
+ "\t\"emptyValue\" : null,\n"
+ "\t\"false\" : false,\n"
+ "\t\"null\" : \"null\",\n"
+ "\t\"number\" : -6200000000000000.0,\n"
+ "\t\"real\" : 1.256,\n"
+ "\t\"uintValue\" : 17\n"
+ "}");
+ Json::Value root;
+ root["emptyValue"] = Json::nullValue;
+ root["false"] = false;
+ root["null"] = "null";
+ root["number"] = -6.2e+15;
+ root["real"] = 1.256;
+ root["uintValue"] = Json::Value(17U);
+
+ const Json::String result = Json::writeString(writer, root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+}
+
+JSONTEST_FIXTURE_LOCAL(StreamWriterTest, writeArrays) {
+ Json::StreamWriterBuilder writer;
+ const Json::String expected("{\n"
+ "\t\"property1\" : \n"
+ "\t[\n"
+ "\t\t\"value1\",\n"
+ "\t\t\"value2\"\n"
+ "\t],\n"
+ "\t\"property2\" : []\n"
+ "}");
+
+ Json::Value root;
+ root["property1"][0] = "value1";
+ root["property1"][1] = "value2";
+ root["property2"] = Json::arrayValue;
+
+ const Json::String result = Json::writeString(writer, root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+}
+
+JSONTEST_FIXTURE_LOCAL(StreamWriterTest, writeNestedObjects) {
+ Json::StreamWriterBuilder writer;
+ const Json::String expected("{\n"
+ "\t\"object1\" : \n"
+ "\t{\n"
+ "\t\t\"bool\" : true,\n"
+ "\t\t\"nested\" : 123\n"
+ "\t},\n"
+ "\t\"object2\" : {}\n"
+ "}");
+
+ Json::Value root, child;
+ child["nested"] = 123;
+ child["bool"] = true;
+ root["object1"] = child;
+ root["object2"] = Json::objectValue;
+
+ const Json::String result = Json::writeString(writer, root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+}
+
+JSONTEST_FIXTURE_LOCAL(StreamWriterTest, multiLineArray) {
+ Json::StreamWriterBuilder wb;
+ wb.settings_["commentStyle"] = "None";
+ {
+ // When wb.settings_["commentStyle"] = "None", the effect of
+ // printing multiple lines will be displayed when there are
+ // more than 20 array members.
+ const Json::String expected("[\n\t0,"
+ "\n\t1,"
+ "\n\t2,"
+ "\n\t3,"
+ "\n\t4,"
+ "\n\t5,"
+ "\n\t6,"
+ "\n\t7,"
+ "\n\t8,"
+ "\n\t9,"
+ "\n\t10,"
+ "\n\t11,"
+ "\n\t12,"
+ "\n\t13,"
+ "\n\t14,"
+ "\n\t15,"
+ "\n\t16,"
+ "\n\t17,"
+ "\n\t18,"
+ "\n\t19,"
+ "\n\t20\n]");
+ Json::Value root;
+ for (Json::ArrayIndex i = 0; i < 21; i++)
+ root[i] = i;
+ const Json::String result = Json::writeString(wb, root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+ }
+ {
+ // Array members do not exceed 21 print effects to render a single line
+ const Json::String expected("[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]");
+ Json::Value root;
+ for (Json::ArrayIndex i = 0; i < 10; i++)
+ root[i] = i;
+ const Json::String result = Json::writeString(wb, root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+ }
+}
+
+JSONTEST_FIXTURE_LOCAL(StreamWriterTest, dropNullPlaceholders) {
Json::StreamWriterBuilder b;
Json::Value nullValue;
b.settings_["dropNullPlaceholders"] = false;
JSONTEST_ASSERT(Json::writeString(b, nullValue).empty());
}
-JSONTEST_FIXTURE(StreamWriterTest, writeZeroes) {
+JSONTEST_FIXTURE_LOCAL(StreamWriterTest, enableYAMLCompatibility) {
+ Json::StreamWriterBuilder b;
+ Json::Value root;
+ root["hello"] = "world";
+
+ b.settings_["indentation"] = "";
+ JSONTEST_ASSERT(Json::writeString(b, root) == "{\"hello\":\"world\"}");
+
+ b.settings_["enableYAMLCompatibility"] = true;
+ JSONTEST_ASSERT(Json::writeString(b, root) == "{\"hello\": \"world\"}");
+
+ b.settings_["enableYAMLCompatibility"] = false;
+ JSONTEST_ASSERT(Json::writeString(b, root) == "{\"hello\":\"world\"}");
+}
+
+JSONTEST_FIXTURE_LOCAL(StreamWriterTest, indentation) {
+ Json::StreamWriterBuilder b;
+ Json::Value root;
+ root["hello"] = "world";
+
+ b.settings_["indentation"] = "";
+ JSONTEST_ASSERT(Json::writeString(b, root) == "{\"hello\":\"world\"}");
+
+ b.settings_["indentation"] = "\t";
+ JSONTEST_ASSERT(Json::writeString(b, root) ==
+ "{\n\t\"hello\" : \"world\"\n}");
+}
+
+JSONTEST_FIXTURE_LOCAL(StreamWriterTest, writeZeroes) {
Json::String binary("hi", 3); // include trailing 0
JSONTEST_ASSERT_EQUAL(3, binary.length());
Json::String expected("\"hi\\u0000\""); // unicoded zero
}
}
+JSONTEST_FIXTURE_LOCAL(StreamWriterTest, unicode) {
+ // Create a Json value containing UTF-8 string with some chars that need
+ // escape (tab,newline).
+ Json::Value root;
+ root["test"] = "\t\n\xF0\x91\xA2\xA1\x3D\xC4\xB3\xF0\x9B\x84\x9B\xEF\xBD\xA7";
+
+ Json::StreamWriterBuilder b;
+
+ // Default settings - should be unicode escaped.
+ JSONTEST_ASSERT(Json::writeString(b, root) ==
+ "{\n\t\"test\" : "
+ "\"\\t\\n\\ud806\\udca1=\\u0133\\ud82c\\udd1b\\uff67\"\n}");
+
+ b.settings_["emitUTF8"] = true;
+
+ // Should not be unicode escaped.
+ JSONTEST_ASSERT(
+ Json::writeString(b, root) ==
+ "{\n\t\"test\" : "
+ "\"\\t\\n\xF0\x91\xA2\xA1=\xC4\xB3\xF0\x9B\x84\x9B\xEF\xBD\xA7\"\n}");
+
+ b.settings_["emitUTF8"] = false;
+
+ // Should be unicode escaped.
+ JSONTEST_ASSERT(Json::writeString(b, root) ==
+ "{\n\t\"test\" : "
+ "\"\\t\\n\\ud806\\udca1=\\u0133\\ud82c\\udd1b\\uff67\"\n}");
+}
+
struct ReaderTest : JsonTest::TestCase {};
-JSONTEST_FIXTURE(ReaderTest, parseWithNoErrors) {
+JSONTEST_FIXTURE_LOCAL(ReaderTest, parseWithNoErrors) {
Json::Reader reader;
Json::Value root;
bool ok = reader.parse("{ \"property\" : \"value\" }", root);
JSONTEST_ASSERT(reader.getStructuredErrors().empty());
}
-JSONTEST_FIXTURE(ReaderTest, parseWithNoErrorsTestingOffsets) {
+JSONTEST_FIXTURE_LOCAL(ReaderTest, parseComment) {
+ Json::Reader reader;
+ Json::Value root;
+ bool ok = reader.parse("{ /*commentBeforeValue*/"
+ " \"property\" : \"value\" }"
+ "//commentAfterValue\n",
+ root);
+ JSONTEST_ASSERT(ok);
+ JSONTEST_ASSERT(reader.getFormattedErrorMessages().empty());
+ JSONTEST_ASSERT(reader.getStructuredErrors().empty());
+}
+
+JSONTEST_FIXTURE_LOCAL(ReaderTest, streamParseWithNoErrors) {
+ Json::Reader reader;
+ std::string styled = "{ \"property\" : \"value\" }";
+ std::istringstream iss(styled);
+ Json::Value root;
+ bool ok = reader.parse(iss, root);
+ JSONTEST_ASSERT(ok);
+ JSONTEST_ASSERT(reader.getFormattedErrorMessages().empty());
+ JSONTEST_ASSERT(reader.getStructuredErrors().empty());
+}
+
+JSONTEST_FIXTURE_LOCAL(ReaderTest, parseWithNoErrorsTestingOffsets) {
Json::Reader reader;
Json::Value root;
bool ok = reader.parse("{ \"property\" : [\"value\", \"value2\"], \"obj\" : "
- "{ \"nested\" : 123, \"bool\" : true}, \"null\" : "
- "null, \"false\" : false }",
+ "{ \"nested\" : -6.2e+15, \"bool\" : true}, \"null\" :"
+ " null, \"false\" : false }",
root);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(reader.getFormattedErrorMessages().empty());
JSONTEST_ASSERT(root["property"][1].getOffsetStart() == 25);
JSONTEST_ASSERT(root["property"][1].getOffsetLimit() == 33);
JSONTEST_ASSERT(root["obj"].getOffsetStart() == 44);
- JSONTEST_ASSERT(root["obj"].getOffsetLimit() == 76);
+ JSONTEST_ASSERT(root["obj"].getOffsetLimit() == 81);
JSONTEST_ASSERT(root["obj"]["nested"].getOffsetStart() == 57);
- JSONTEST_ASSERT(root["obj"]["nested"].getOffsetLimit() == 60);
- JSONTEST_ASSERT(root["obj"]["bool"].getOffsetStart() == 71);
- JSONTEST_ASSERT(root["obj"]["bool"].getOffsetLimit() == 75);
- JSONTEST_ASSERT(root["null"].getOffsetStart() == 87);
- JSONTEST_ASSERT(root["null"].getOffsetLimit() == 91);
- JSONTEST_ASSERT(root["false"].getOffsetStart() == 103);
- JSONTEST_ASSERT(root["false"].getOffsetLimit() == 108);
+ JSONTEST_ASSERT(root["obj"]["nested"].getOffsetLimit() == 65);
+ JSONTEST_ASSERT(root["obj"]["bool"].getOffsetStart() == 76);
+ JSONTEST_ASSERT(root["obj"]["bool"].getOffsetLimit() == 80);
+ JSONTEST_ASSERT(root["null"].getOffsetStart() == 92);
+ JSONTEST_ASSERT(root["null"].getOffsetLimit() == 96);
+ JSONTEST_ASSERT(root["false"].getOffsetStart() == 108);
+ JSONTEST_ASSERT(root["false"].getOffsetLimit() == 113);
JSONTEST_ASSERT(root.getOffsetStart() == 0);
- JSONTEST_ASSERT(root.getOffsetLimit() == 110);
+ JSONTEST_ASSERT(root.getOffsetLimit() == 115);
}
-JSONTEST_FIXTURE(ReaderTest, parseWithOneError) {
+JSONTEST_FIXTURE_LOCAL(ReaderTest, parseWithOneError) {
Json::Reader reader;
Json::Value root;
bool ok = reader.parse("{ \"property\" :: \"value\" }", root);
"Syntax error: value, object or array expected.");
}
-JSONTEST_FIXTURE(ReaderTest, parseChineseWithOneError) {
+JSONTEST_FIXTURE_LOCAL(ReaderTest, strictModeParseNumber) {
+ Json::Features feature;
+ Json::Reader reader(feature.strictMode());
+ Json::Value root;
+ bool ok = reader.parse("123", root);
+ JSONTEST_ASSERT(!ok);
+ JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
+ "* Line 1, Column 1\n"
+ " A valid JSON document must be either an array or"
+ " an object value.\n");
+ std::vector<Json::Reader::StructuredError> errors =
+ reader.getStructuredErrors();
+ JSONTEST_ASSERT(errors.size() == 1);
+ JSONTEST_ASSERT(errors.at(0).offset_start == 0);
+ JSONTEST_ASSERT(errors.at(0).offset_limit == 3);
+ JSONTEST_ASSERT(errors.at(0).message ==
+ "A valid JSON document must be either an array or"
+ " an object value.");
+}
+
+JSONTEST_FIXTURE_LOCAL(ReaderTest, parseChineseWithOneError) {
Json::Reader reader;
Json::Value root;
bool ok = reader.parse("{ \"pr佐藤erty\" :: \"value\" }", root);
"Syntax error: value, object or array expected.");
}
-JSONTEST_FIXTURE(ReaderTest, parseWithDetailError) {
+JSONTEST_FIXTURE_LOCAL(ReaderTest, parseWithDetailError) {
Json::Reader reader;
Json::Value root;
bool ok = reader.parse("{ \"property\" : \"v\\alue\" }", root);
JSONTEST_ASSERT(errors.at(0).message == "Bad escape sequence in string");
}
+JSONTEST_FIXTURE_LOCAL(ReaderTest, pushErrorTest) {
+ Json::Reader reader;
+ Json::Value root;
+ {
+ bool ok = reader.parse("{ \"AUTHOR\" : 123 }", root);
+ JSONTEST_ASSERT(ok);
+ if (!root["AUTHOR"].isString()) {
+ ok = reader.pushError(root["AUTHOR"], "AUTHOR must be a string");
+ }
+ JSONTEST_ASSERT(ok);
+ JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
+ "* Line 1, Column 14\n"
+ " AUTHOR must be a string\n");
+ }
+ {
+ bool ok = reader.parse("{ \"AUTHOR\" : 123 }", root);
+ JSONTEST_ASSERT(ok);
+ if (!root["AUTHOR"].isString()) {
+ ok = reader.pushError(root["AUTHOR"], "AUTHOR must be a string",
+ root["AUTHOR"]);
+ }
+ JSONTEST_ASSERT(ok);
+ JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
+ "* Line 1, Column 14\n"
+ " AUTHOR must be a string\n"
+ "See Line 1, Column 14 for detail.\n");
+ }
+}
+
struct CharReaderTest : JsonTest::TestCase {};
-JSONTEST_FIXTURE(CharReaderTest, parseWithNoErrors) {
+JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseWithNoErrors) {
Json::CharReaderBuilder b;
Json::CharReader* reader(b.newCharReader());
Json::String errs;
delete reader;
}
-JSONTEST_FIXTURE(CharReaderTest, parseWithNoErrorsTestingOffsets) {
+JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseWithNoErrorsTestingOffsets) {
Json::CharReaderBuilder b;
Json::CharReader* reader(b.newCharReader());
Json::String errs;
Json::Value root;
char const doc[] = "{ \"property\" : [\"value\", \"value2\"], \"obj\" : "
- "{ \"nested\" : 123, \"bool\" : true}, \"null\" : "
+ "{ \"nested\" : -6.2e+15, \"bool\" : true}, \"null\" : "
"null, \"false\" : false }";
bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
JSONTEST_ASSERT(ok);
delete reader;
}
-JSONTEST_FIXTURE(CharReaderTest, parseWithOneError) {
+JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseWithOneError) {
Json::CharReaderBuilder b;
Json::CharReader* reader(b.newCharReader());
Json::String errs;
delete reader;
}
-JSONTEST_FIXTURE(CharReaderTest, parseChineseWithOneError) {
+JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseChineseWithOneError) {
Json::CharReaderBuilder b;
Json::CharReader* reader(b.newCharReader());
Json::String errs;
delete reader;
}
-JSONTEST_FIXTURE(CharReaderTest, parseWithDetailError) {
+JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseWithDetailError) {
Json::CharReaderBuilder b;
Json::CharReader* reader(b.newCharReader());
Json::String errs;
delete reader;
}
-JSONTEST_FIXTURE(CharReaderTest, parseWithStackLimit) {
+JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseWithStackLimit) {
Json::CharReaderBuilder b;
Json::Value root;
char const doc[] = "{ \"property\" : \"value\" }";
}
}
+JSONTEST_FIXTURE_LOCAL(CharReaderTest, testOperator) {
+ const std::string styled = "{ \"property\" : \"value\" }";
+ std::istringstream iss(styled);
+ Json::Value root;
+ iss >> root;
+ JSONTEST_ASSERT_EQUAL("value", root["property"]);
+}
+
struct CharReaderStrictModeTest : JsonTest::TestCase {};
-JSONTEST_FIXTURE(CharReaderStrictModeTest, dupKeys) {
+JSONTEST_FIXTURE_LOCAL(CharReaderStrictModeTest, dupKeys) {
Json::CharReaderBuilder b;
Json::Value root;
char const doc[] =
}
struct CharReaderFailIfExtraTest : JsonTest::TestCase {};
-JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue164) {
+JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, issue164) {
// This is interpreted as a string value followed by a colon.
Json::CharReaderBuilder b;
Json::Value root;
Json::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_STRING_EQUAL("* Line 1, Column 13\n"
+ " Extra non-whitespace after JSON value.\n",
+ errs);
+ JSONTEST_ASSERT_EQUAL("property", root);
+ delete reader;
+ }
+ {
+ b.strictMode(&b.settings_);
+ Json::CharReader* reader(b.newCharReader());
+ Json::String errs;
+ bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+ JSONTEST_ASSERT(!ok);
+ JSONTEST_ASSERT_STRING_EQUAL("* Line 1, Column 13\n"
+ " Extra non-whitespace after JSON value.\n",
+ errs);
JSONTEST_ASSERT_EQUAL("property", root);
delete reader;
}
{
- b.settings_["failIfExtra"] = false;
b.strictMode(&b.settings_);
+ b.settings_["failIfExtra"] = false;
Json::CharReader* reader(b.newCharReader());
Json::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_STRING_EQUAL(
+ "* Line 1, Column 1\n"
+ " A valid JSON document must be either an array or an object value.\n",
+ errs);
JSONTEST_ASSERT_EQUAL("property", root);
delete reader;
}
}
-JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue107) {
+JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, issue107) {
// This is interpreted as an int value followed by a colon.
Json::CharReaderBuilder b;
Json::Value root;
JSONTEST_ASSERT_EQUAL(1, root.asInt());
delete reader;
}
-JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterObject) {
+JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, commentAfterObject) {
Json::CharReaderBuilder b;
Json::Value root;
{
delete reader;
}
}
-JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterArray) {
+JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, commentAfterArray) {
Json::CharReaderBuilder b;
Json::Value root;
char const doc[] = "[ \"property\" , \"value\" ] //trailing\n//comment\n";
JSONTEST_ASSERT_EQUAL("value", root[1u]);
delete reader;
}
-JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterBool) {
+JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, commentAfterBool) {
Json::CharReaderBuilder b;
Json::Value root;
char const doc[] = " true /*trailing\ncomment*/";
}
struct CharReaderAllowDropNullTest : JsonTest::TestCase {};
-JSONTEST_FIXTURE(CharReaderAllowDropNullTest, issue178) {
+JSONTEST_FIXTURE_LOCAL(CharReaderAllowDropNullTest, issue178) {
Json::CharReaderBuilder b;
b.settings_["allowDroppedNullPlaceholders"] = true;
Json::Value root;
delete reader;
}
+struct CharReaderAllowNumericKeysTest : JsonTest::TestCase {};
+
+JSONTEST_FIXTURE_LOCAL(CharReaderAllowNumericKeysTest, allowNumericKeys) {
+ Json::CharReaderBuilder b;
+ b.settings_["allowNumericKeys"] = true;
+ Json::Value root;
+ Json::String errs;
+ Json::CharReader* reader(b.newCharReader());
+ char const doc[] = "{15:true,-16:true,12.01:true}";
+ bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+ JSONTEST_ASSERT(ok);
+ JSONTEST_ASSERT_STRING_EQUAL("", errs);
+ JSONTEST_ASSERT_EQUAL(3u, root.size());
+ JSONTEST_ASSERT_EQUAL(true, root.get("15", false));
+ JSONTEST_ASSERT_EQUAL(true, root.get("-16", false));
+ JSONTEST_ASSERT_EQUAL(true, root.get("12.01", false));
+ delete reader;
+}
+
struct CharReaderAllowSingleQuotesTest : JsonTest::TestCase {};
-JSONTEST_FIXTURE(CharReaderAllowSingleQuotesTest, issue182) {
+JSONTEST_FIXTURE_LOCAL(CharReaderAllowSingleQuotesTest, issue182) {
Json::CharReaderBuilder b;
b.settings_["allowSingleQuotes"] = true;
Json::Value root;
struct CharReaderAllowZeroesTest : JsonTest::TestCase {};
-JSONTEST_FIXTURE(CharReaderAllowZeroesTest, issue176) {
+JSONTEST_FIXTURE_LOCAL(CharReaderAllowZeroesTest, issue176) {
Json::CharReaderBuilder b;
b.settings_["allowSingleQuotes"] = true;
Json::Value root;
struct CharReaderAllowSpecialFloatsTest : JsonTest::TestCase {};
-JSONTEST_FIXTURE(CharReaderAllowSpecialFloatsTest, issue209) {
+JSONTEST_FIXTURE_LOCAL(CharReaderAllowSpecialFloatsTest, issue209) {
Json::CharReaderBuilder b;
b.settings_["allowSpecialFloats"] = true;
Json::Value root;
Json::String errs;
Json::CharReader* reader(b.newCharReader());
{
- char const doc[] = "{\"a\":NaN,\"b\":Infinity,\"c\":-Infinity}";
+ char const doc[] =
+ "{\"a\":NaN,\"b\":Infinity,\"c\":-Infinity,\"d\":+Infinity}";
bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
- JSONTEST_ASSERT_EQUAL(3u, root.size());
+ JSONTEST_ASSERT_EQUAL(4u, root.size());
double n = root["a"].asDouble();
JSONTEST_ASSERT(std::isnan(n));
JSONTEST_ASSERT_EQUAL(std::numeric_limits<double>::infinity(),
root.get("b", 0.0));
JSONTEST_ASSERT_EQUAL(-std::numeric_limits<double>::infinity(),
root.get("c", 0.0));
+ JSONTEST_ASSERT_EQUAL(std::numeric_limits<double>::infinity(),
+ root.get("d", 0.0));
}
struct TestData {
{__LINE__, false, "{\"a\":.Infinity}"}, //
{__LINE__, false, "{\"a\":_Infinity}"}, //
{__LINE__, false, "{\"a\":_nfinity}"}, //
- {__LINE__, true, "{\"a\":-Infinity}"} //
+ {__LINE__, true, "{\"a\":-Infinity}"}, //
+ {__LINE__, true, "{\"a\":+Infinity}"} //
};
for (const auto& td : test_data) {
bool ok = reader->parse(&*td.in.begin(), &*td.in.begin() + td.in.size(),
}
{
- char const doc[] = "{\"posInf\": Infinity, \"NegInf\": -Infinity}";
+ char const doc[] = "{\"posInf\": +Infinity, \"NegInf\": -Infinity}";
bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
delete reader;
}
+struct EscapeSequenceTest : JsonTest::TestCase {};
+
+JSONTEST_FIXTURE_LOCAL(EscapeSequenceTest, readerParseEscapeSequence) {
+ Json::Reader reader;
+ Json::Value root;
+ bool ok = reader.parse("[\"\\\"\",\"\\/\",\"\\\\\",\"\\b\","
+ "\"\\f\",\"\\n\",\"\\r\",\"\\t\","
+ "\"\\u0278\",\"\\ud852\\udf62\"]\n",
+ root);
+ JSONTEST_ASSERT(ok);
+ JSONTEST_ASSERT(reader.getFormattedErrorMessages().empty());
+ JSONTEST_ASSERT(reader.getStructuredErrors().empty());
+}
+
+JSONTEST_FIXTURE_LOCAL(EscapeSequenceTest, charReaderParseEscapeSequence) {
+ Json::CharReaderBuilder b;
+ Json::CharReader* reader(b.newCharReader());
+ Json::Value root;
+ Json::String errs;
+ char const doc[] = "[\"\\\"\",\"\\/\",\"\\\\\",\"\\b\","
+ "\"\\f\",\"\\n\",\"\\r\",\"\\t\","
+ "\"\\u0278\",\"\\ud852\\udf62\"]";
+ bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+ JSONTEST_ASSERT(ok);
+ JSONTEST_ASSERT(errs.empty());
+ delete reader;
+}
+
+JSONTEST_FIXTURE_LOCAL(EscapeSequenceTest, writeEscapeSequence) {
+ Json::FastWriter writer;
+ const Json::String expected("[\"\\\"\",\"\\\\\",\"\\b\","
+ "\"\\f\",\"\\n\",\"\\r\",\"\\t\","
+ "\"\\u0278\",\"\\ud852\\udf62\"]\n");
+ Json::Value root;
+ root[0] = "\"";
+ root[1] = "\\";
+ root[2] = "\b";
+ root[3] = "\f";
+ root[4] = "\n";
+ root[5] = "\r";
+ root[6] = "\t";
+ root[7] = "ɸ";
+ root[8] = "𤭢";
+ const Json::String result = writer.write(root);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+}
+
struct BuilderTest : JsonTest::TestCase {};
-JSONTEST_FIXTURE(BuilderTest, settings) {
+JSONTEST_FIXTURE_LOCAL(BuilderTest, settings) {
{
Json::Value errs;
Json::CharReaderBuilder rb;
struct IteratorTest : JsonTest::TestCase {};
-JSONTEST_FIXTURE(IteratorTest, distance) {
+JSONTEST_FIXTURE_LOCAL(IteratorTest, convert) {
+ Json::Value j;
+ const Json::Value& cj = j;
+ auto it = j.begin();
+ Json::Value::const_iterator cit;
+ cit = it;
+ JSONTEST_ASSERT(cit == cj.begin());
+}
+
+JSONTEST_FIXTURE_LOCAL(IteratorTest, decrement) {
Json::Value json;
json["k1"] = "a";
json["k2"] = "b";
- int dist = 0;
- Json::String str;
- for (Json::ValueIterator it = json.begin(); it != json.end(); ++it) {
- dist = it - json.begin();
- str = it->asString().c_str();
+ std::vector<std::string> values;
+ for (auto it = json.end(); it != json.begin();) {
+ --it;
+ values.push_back(it->asString());
+ }
+ JSONTEST_ASSERT((values == std::vector<std::string>{"b", "a"}));
+}
+
+JSONTEST_FIXTURE_LOCAL(IteratorTest, reverseIterator) {
+ Json::Value json;
+ json["k1"] = "a";
+ json["k2"] = "b";
+ std::vector<std::string> values;
+ using Iter = decltype(json.begin());
+ auto re = std::reverse_iterator<Iter>(json.begin());
+ for (auto it = std::reverse_iterator<Iter>(json.end()); it != re; ++it) {
+ values.push_back(it->asString());
+ }
+ JSONTEST_ASSERT((values == std::vector<std::string>{"b", "a"}));
+}
+
+JSONTEST_FIXTURE_LOCAL(IteratorTest, distance) {
+ {
+ Json::Value json;
+ json["k1"] = "a";
+ json["k2"] = "b";
+ int i = 0;
+ auto it = json.begin();
+ for (;; ++it, ++i) {
+ auto dist = it - json.begin();
+ JSONTEST_ASSERT_EQUAL(i, dist);
+ if (it == json.end())
+ break;
+ }
+ }
+ {
+ Json::Value empty;
+ JSONTEST_ASSERT_EQUAL(empty.end() - empty.end(), 0);
+ JSONTEST_ASSERT_EQUAL(empty.end() - empty.begin(), 0);
+ }
+}
+
+JSONTEST_FIXTURE_LOCAL(IteratorTest, nullValues) {
+ {
+ Json::Value json;
+ auto end = json.end();
+ auto endCopy = end;
+ JSONTEST_ASSERT(endCopy == end);
+ endCopy = end;
+ JSONTEST_ASSERT(endCopy == end);
}
- JSONTEST_ASSERT_EQUAL(1, dist);
- JSONTEST_ASSERT_STRING_EQUAL("b", str);
+ {
+ // Same test, now with const Value.
+ const Json::Value json;
+ auto end = json.end();
+ auto endCopy = end;
+ JSONTEST_ASSERT(endCopy == end);
+ endCopy = end;
+ JSONTEST_ASSERT(endCopy == end);
+ }
+}
+
+JSONTEST_FIXTURE_LOCAL(IteratorTest, staticStringKey) {
+ Json::Value json;
+ json[Json::StaticString("k1")] = "a";
+ JSONTEST_ASSERT_EQUAL(Json::Value("k1"), json.begin().key());
}
-JSONTEST_FIXTURE(IteratorTest, names) {
+JSONTEST_FIXTURE_LOCAL(IteratorTest, names) {
Json::Value json;
json["k1"] = "a";
json["k2"] = "b";
JSONTEST_ASSERT(it != json.end());
JSONTEST_ASSERT_EQUAL(Json::Value("k1"), it.key());
JSONTEST_ASSERT_STRING_EQUAL("k1", it.name());
+ JSONTEST_ASSERT_STRING_EQUAL("k1", it.memberName());
JSONTEST_ASSERT_EQUAL(-1, it.index());
++it;
JSONTEST_ASSERT(it != json.end());
JSONTEST_ASSERT_EQUAL(Json::Value("k2"), it.key());
JSONTEST_ASSERT_STRING_EQUAL("k2", it.name());
+ JSONTEST_ASSERT_STRING_EQUAL("k2", it.memberName());
JSONTEST_ASSERT_EQUAL(-1, it.index());
++it;
JSONTEST_ASSERT(it == json.end());
}
-JSONTEST_FIXTURE(IteratorTest, indexes) {
+JSONTEST_FIXTURE_LOCAL(IteratorTest, indexes) {
Json::Value json;
json[0] = "a";
json[1] = "b";
JSONTEST_ASSERT(it == json.end());
}
-JSONTEST_FIXTURE(IteratorTest, const) {
+JSONTEST_FIXTURE_LOCAL(IteratorTest, constness) {
Json::Value const v;
JSONTEST_ASSERT_THROWS(
- Json::Value::iterator it(v.begin()) // Compile, but throw.
- );
+ Json::Value::iterator it(v.begin())); // Compile, but throw.
Json::Value value;
struct RValueTest : JsonTest::TestCase {};
-JSONTEST_FIXTURE(RValueTest, moveConstruction) {
+JSONTEST_FIXTURE_LOCAL(RValueTest, moveConstruction) {
Json::Value json;
json["key"] = "value";
Json::Value moved = std::move(json);
// Build and run the fuzz test without any fuzzer, so that it's guaranteed not
// go out of date, even if it's never run as an actual fuzz test.
-JSONTEST_FIXTURE(FuzzTest, fuzzDoesntCrash) {
+JSONTEST_FIXTURE_LOCAL(FuzzTest, fuzzDoesntCrash) {
const std::string example = "{}";
JSONTEST_ASSERT_EQUAL(
0,
int main(int argc, const char* argv[]) {
JsonTest::Runner runner;
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, memberCount);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, objects);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, arrays);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, arrayIssue252);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, null);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, strings);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, bools);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, integers);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, nonIntegers);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, compareNull);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, compareInt);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, compareUInt);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, compareDouble);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, compareString);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, compareBoolean);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, compareArray);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, compareObject);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, compareType);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, CopyObject);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, offsetAccessors);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, typeChecksThrowExceptions);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, StaticString);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, WideString);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, CommentBefore);
- // JSONTEST_REGISTER_FIXTURE(runner, ValueTest, nulls);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, zeroes);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, zeroesInKeys);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, specialFloats);
- JSONTEST_REGISTER_FIXTURE(runner, ValueTest, precision);
-
- JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders);
- JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, dropNullPlaceholders);
- JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, writeZeroes);
-
- JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithNoErrors);
- JSONTEST_REGISTER_FIXTURE(runner, ReaderTest,
- parseWithNoErrorsTestingOffsets);
- JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithOneError);
- JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseChineseWithOneError);
- 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, CharReaderStrictModeTest, dupKeys);
-
- 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, CharReaderAllowDropNullTest, issue178);
-
- JSONTEST_REGISTER_FIXTURE(runner, CharReaderAllowSingleQuotesTest, issue182);
-
- JSONTEST_REGISTER_FIXTURE(runner, CharReaderAllowZeroesTest, issue176);
-
- JSONTEST_REGISTER_FIXTURE(runner, CharReaderAllowSpecialFloatsTest, issue209);
-
- JSONTEST_REGISTER_FIXTURE(runner, BuilderTest, settings);
-
- JSONTEST_REGISTER_FIXTURE(runner, IteratorTest, distance);
- JSONTEST_REGISTER_FIXTURE(runner, IteratorTest, names);
- JSONTEST_REGISTER_FIXTURE(runner, IteratorTest, indexes);
- JSONTEST_REGISTER_FIXTURE(runner, IteratorTest, const);
-
- JSONTEST_REGISTER_FIXTURE(runner, RValueTest, moveConstruction);
-
- JSONTEST_REGISTER_FIXTURE(runner, FuzzTest, fuzzDoesntCrash);
+
+ for (auto it = local_.begin(); it != local_.end(); it++) {
+ runner.add(*it);
+ }
return runner.runCommandLine(argc, argv);
}
+struct MemberTemplateAs : JsonTest::TestCase {
+ template <typename T, typename F>
+ JsonTest::TestResult& EqEval(T v, F f) const {
+ const Json::Value j = v;
+ return JSONTEST_ASSERT_EQUAL(j.as<T>(), f(j));
+ }
+};
+
+JSONTEST_FIXTURE_LOCAL(MemberTemplateAs, BehavesSameAsNamedAs) {
+ const Json::Value jstr = "hello world";
+ JSONTEST_ASSERT_STRING_EQUAL(jstr.as<const char*>(), jstr.asCString());
+ JSONTEST_ASSERT_STRING_EQUAL(jstr.as<Json::String>(), jstr.asString());
+#ifdef JSON_USE_CPPTL
+ JSONTEST_ASSERT_STRING_EQUAL(js.as<CppTL::ConstString>(), js.asConstString());
+#endif
+ EqEval(Json::Int(64), [](const Json::Value& j) { return j.asInt(); });
+ EqEval(Json::UInt(64), [](const Json::Value& j) { return j.asUInt(); });
+#if defined(JSON_HAS_INT64)
+ EqEval(Json::Int64(64), [](const Json::Value& j) { return j.asInt64(); });
+ EqEval(Json::UInt64(64), [](const Json::Value& j) { return j.asUInt64(); });
+#endif // if defined(JSON_HAS_INT64)
+ EqEval(Json::LargestInt(64),
+ [](const Json::Value& j) { return j.asLargestInt(); });
+ EqEval(Json::LargestUInt(64),
+ [](const Json::Value& j) { return j.asLargestUInt(); });
+
+ EqEval(69.69f, [](const Json::Value& j) { return j.asFloat(); });
+ EqEval(69.69, [](const Json::Value& j) { return j.asDouble(); });
+ EqEval(false, [](const Json::Value& j) { return j.asBool(); });
+ EqEval(true, [](const Json::Value& j) { return j.asBool(); });
+}
+
+class MemberTemplateIs : public JsonTest::TestCase {};
+
+JSONTEST_FIXTURE_LOCAL(MemberTemplateIs, BehavesSameAsNamedIs) {
+ const Json::Value values[] = {true, 142, 40.63, "hello world"};
+ for (const Json::Value& j : values) {
+ JSONTEST_ASSERT_EQUAL(j.is<bool>(), j.isBool());
+ JSONTEST_ASSERT_EQUAL(j.is<Json::Int>(), j.isInt());
+ JSONTEST_ASSERT_EQUAL(j.is<Json::Int64>(), j.isInt64());
+ JSONTEST_ASSERT_EQUAL(j.is<Json::UInt>(), j.isUInt());
+ JSONTEST_ASSERT_EQUAL(j.is<Json::UInt64>(), j.isUInt64());
+ JSONTEST_ASSERT_EQUAL(j.is<double>(), j.isDouble());
+ JSONTEST_ASSERT_EQUAL(j.is<Json::String>(), j.isString());
+ }
+}
+
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif