Support dropbox/json11.

This commit is contained in:
hotwatermorning 2016-01-26 13:19:40 +09:00
parent 4c6bf5d12c
commit 04dc77b87c
4 changed files with 814 additions and 1 deletions

View File

@ -2,6 +2,7 @@ cmake_minimum_required (VERSION 2.6)
project (valijson)
SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
SET(CMAKE_CXX_FLAGS "-std=c++11")
add_definitions(-DBOOST_ALL_DYN_LINK)
set(Boost_USE_STATIC_LIBS OFF)
@ -17,6 +18,10 @@ add_library(jsoncpp
thirdparty/jsoncpp-0.9.4/src/lib_json/json_writer.cpp
)
add_library(json11
thirdparty/json11-2016-01-26/json11.cpp
)
# Build local gtest
set(gtest_force_shared_crt ON)
add_subdirectory(thirdparty/gtest-1.7.0)
@ -28,6 +33,7 @@ include_directories(
thirdparty/jsoncpp-0.9.4/include
thirdparty/rapidjson-0.1/include
thirdparty/picojson-1.3.0
thirdparty/json11-2016-01-26
${Boost_INCLUDE_DIRS}
)
@ -50,6 +56,7 @@ add_executable(test_suite
tests/test_property_tree_adapter.cpp
tests/test_rapidjson_adapter.cpp
tests/test_picojson_adapter.cpp
tests/test_json11_adapter.cpp
tests/test_validation_errors.cpp
tests/test_validator.cpp
)
@ -59,7 +66,7 @@ set_target_properties(test_suite
PROPERTIES COMPILE_DEFINITIONS "PICOJSON_USE_INT64"
)
set(TEST_LIBS gtest gtest_main jsoncpp)
set(TEST_LIBS gtest gtest_main jsoncpp json11)
target_link_libraries(test_suite ${TEST_LIBS} ${Boost_LIBRARIES})
target_link_libraries(custom_schema ${Boost_LIBRARIES})

View File

@ -0,0 +1,685 @@
/**
* @file
*
* @brief Adapter implementation for the Json11 parser library.
*
* Include this file in your program to enable support for Json11.
*
* This file defines the following classes (not in this order):
* - Json11Adapter
* - Json11Array
* - Json11ArrayValueIterator
* - Json11FrozenValue
* - Json11Object
* - Json11ObjectMember
* - Json11ObjectMemberIterator
* - Json11Value
*
* Due to the dependencies that exist between these classes, the ordering of
* class declarations and definitions may be a bit confusing. The best place to
* start is Json11Adapter. This class definition is actually very small,
* since most of the functionality is inherited from the BasicAdapter class.
* Most of the classes in this file are provided as template arguments to the
* inherited BasicAdapter class.
*/
#ifndef __VALIJSON_ADAPTERS_JSON11_ADAPTER_HPP
#define __VALIJSON_ADAPTERS_JSON11_ADAPTER_HPP
#include <string>
#include <json11.hpp>
#include <boost/optional.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <valijson/adapters/adapter.hpp>
#include <valijson/adapters/basic_adapter.hpp>
#include <valijson/adapters/frozen_value.hpp>
namespace valijson {
namespace adapters {
class Json11Adapter;
class Json11ArrayValueIterator;
class Json11ObjectMemberIterator;
typedef std::pair<std::string, Json11Adapter> Json11ObjectMember;
/**
* @brief Light weight wrapper for a Json11 array value.
*
* This class is light weight wrapper for a Json11 array. It provides a
* minimum set of container functions and typedefs that allow it to be used as
* an iterable container.
*
* An instance of this class contains a single reference to the underlying
* Json11 value, assumed to be an array, so there is very little overhead
* associated with copy construction and passing by value.
*/
class Json11Array
{
public:
typedef Json11ArrayValueIterator const_iterator;
typedef Json11ArrayValueIterator iterator;
/// Construct a Json11Array referencing an empty array.
Json11Array()
: value(emptyArray()) { }
/**
* @brief Construct a Json11Array referencing a specific Json11
* value.
*
* @param value reference to a Json11 value
*
* Note that this constructor will throw an exception if the value is not
* an array.
*/
Json11Array(const json11::Json &value)
: value(value)
{
if (!value.is_array()) {
throw std::runtime_error("Value is not an array.");
}
}
/**
* @brief Return an iterator for the first element of the array.
*
* The iterator return by this function is effectively the iterator
* returned by the underlying Json11 implementation.
*/
Json11ArrayValueIterator begin() const;
/**
* @brief Return an iterator for one-past the last element of the array.
*
* The iterator return by this function is effectively the iterator
* returned by the underlying Json11 implementation.
*/
Json11ArrayValueIterator end() const;
/// Return the number of elements in the array
size_t size() const
{
const json11::Json::array &array = value.array_items();
return array.size();
}
private:
/**
* @brief Return a reference to a Json11 value that is an empty array.
*
* Note that the value returned by this function is a singleton.
*/
static const json11::Json & emptyArray()
{
static const json11::Json array((json11::Json::array()));
return array;
}
/// Reference to the contained value
const json11::Json &value;
};
/**
* @brief Light weight wrapper for a Json11 object.
*
* This class is light weight wrapper for a Json11 object. It provides a
* minimum set of container functions and typedefs that allow it to be used as
* an iterable container.
*
* An instance of this class contains a single reference to the underlying
* Json11 value, assumed to be an object, so there is very little overhead
* associated with copy construction and passing by value.
*/
class Json11Object
{
public:
typedef Json11ObjectMemberIterator const_iterator;
typedef Json11ObjectMemberIterator iterator;
/// Construct a Json11Object referencing an empty object singleton.
Json11Object()
: value(emptyObject()) { }
/**
* @brief Construct a Json11Object referencing a specific Json11
* value.
*
* @param value reference to a Json11 value
*
* Note that this constructor will throw an exception if the value is not
* an object.
*/
Json11Object(const json11::Json &value)
: value(value)
{
if (!value.is_object()) {
throw std::runtime_error("Value is not an object.");
}
}
/**
* @brief Return an iterator for this first object member
*
* The iterator return by this function is effectively a wrapper around
* the iterator value returned by the underlying Json11 implementation.
*/
Json11ObjectMemberIterator begin() const;
/**
* @brief Return an iterator for an invalid object member that indicates
* the end of the collection.
*
* The iterator return by this function is effectively a wrapper around
* the iterator value returned by the underlying Json11 implementation.
*/
Json11ObjectMemberIterator end() const;
/**
* @brief Return an iterator for the object member with the specified
* property name.
*
* If an object member with the specified name does not exist, the iterator
* returned will be the same as the iterator returned by the end() function.
*
* @param propertyName property name to search for
*/
Json11ObjectMemberIterator find(const std::string &propertyName) const;
/// Returns the number of members belonging to this object.
size_t size() const
{
const json11::Json::object &object = value.object_items();
return object.size();
}
private:
/**
* @brief Return a reference to a Json11 value that is empty object.
*
* Note that the value returned by this function is a singleton.
*/
static const json11::Json & emptyObject()
{
static const json11::Json object((json11::Json::object()));
return object;
}
/// Reference to the contained object
const json11::Json &value;
};
/**
* @brief Stores an independent copy of a Json11 value.
*
* This class allows a Json11 value to be stored independent of its original
* document. Json11 makes this easy to do, as it does not perform any
* custom memory management.
*
* @see FrozenValue
*/
class Json11FrozenValue: public FrozenValue
{
public:
/**
* @brief Make a copy of a Json11 value
*
* @param source the Json11 value to be copied
*/
Json11FrozenValue(const json11::Json &source)
: value(source) { }
virtual FrozenValue * clone() const
{
return new Json11FrozenValue(value);
}
virtual bool equalTo(const Adapter &other, bool strict) const;
private:
/// Stored Json11 value
json11::Json value;
};
/**
* @brief Light weight wrapper for a Json11 value.
*
* This class is passed as an argument to the BasicAdapter template class,
* and is used to provide access to a Json11 value. This class is responsible
* for the mechanics of actually reading a Json11 value, whereas the
* BasicAdapter class is responsible for the semantics of type comparisons
* and conversions.
*
* The functions that need to be provided by this class are defined implicitly
* by the implementation of the BasicAdapter template class.
*
* @see BasicAdapter
*/
class Json11Value
{
public:
/// Construct a wrapper for the empty object singleton
Json11Value()
: value(emptyObject()) { }
/// Construct a wrapper for a specific Json11 value
Json11Value(const json11::Json &value)
: value(value) { }
/**
* @brief Create a new Json11FrozenValue instance that contains the
* value referenced by this Json11Value instance.
*
* @returns pointer to a new Json11FrozenValue instance, belonging to the
* caller.
*/
FrozenValue * freeze() const
{
return new Json11FrozenValue(value);
}
/**
* @brief Optionally return a Json11Array instance.
*
* If the referenced Json11 value is an array, this function will return
* a boost::optional containing a Json11Array instance referencing the
* array.
*
* Otherwise it will return boost::none.
*/
boost::optional<Json11Array> getArrayOptional() const
{
if (value.is_array()) {
return boost::make_optional(Json11Array(value));
}
return boost::none;
}
/**
* @brief Retrieve the number of elements in the array
*
* If the referenced Json11 value is an array, this function will
* retrieve the number of elements in the array and store it in the output
* variable provided.
*
* @param result reference to size_t to set with result
*
* @returns true if the number of elements was retrieved, false otherwise.
*/
bool getArraySize(size_t &result) const
{
if (value.is_array()) {
const json11::Json::array& array = value.array_items();
result = array.size();
return true;
}
return false;
}
bool getBool(bool &result) const
{
if (value.is_bool()) {
result = value.bool_value();
return true;
}
return false;
}
bool getDouble(double &result) const
{
if (value.is_number()) {
result = value.number_value();
return true;
}
return false;
}
bool getInteger(int64_t &result) const
{
if(isInteger()) {
result = value.int_value();
return true;
}
return false;
}
/**
* @brief Optionally return a Json11Object instance.
*
* If the referenced Json11 value is an object, this function will return a
* boost::optional containing a Json11Object instance referencing the
* object.
*
* Otherwise it will return boost::none.
*/
boost::optional<Json11Object> getObjectOptional() const
{
if (value.is_object()) {
return boost::make_optional(Json11Object(value));
}
return boost::none;
}
/**
* @brief Retrieve the number of members in the object
*
* If the referenced Json11 value is an object, this function will
* retrieve the number of members in the object and store it in the output
* variable provided.
*
* @param result reference to size_t to set with result
*
* @returns true if the number of members was retrieved, false otherwise.
*/
bool getObjectSize(size_t &result) const
{
if (value.is_object()) {
const json11::Json::object &object = value.object_items();
result = object.size();
return true;
}
return false;
}
bool getString(std::string &result) const
{
if (value.is_string()) {
result = value.string_value();
return true;
}
return false;
}
static bool hasStrictTypes()
{
return true;
}
bool isArray() const
{
return value.is_array();
}
bool isBool() const
{
return value.is_bool();
}
bool isDouble() const
{
return value.is_number();
}
bool isInteger() const
{
return value.is_number()
&& static_cast<int>(value.number_value()) == value.number_value();
}
bool isNull() const
{
return value.is_null();
}
bool isNumber() const
{
return value.is_number();
}
bool isObject() const
{
return value.is_object();
}
bool isString() const
{
return value.is_string();
}
private:
/// Return a reference to an empty object singleton
static const json11::Json & emptyObject()
{
static const json11::Json object((json11::Json::object()));
return object;
}
/// Reference to the contained Json11 value.
const json11::Json &value;
};
/**
* @brief An implementation of the Adapter interface supporting Json11.
*
* This class is defined in terms of the BasicAdapter template class, which
* helps to ensure that all of the Adapter implementations behave consistently.
*
* @see Adapter
* @see BasicAdapter
*/
class Json11Adapter:
public BasicAdapter<Json11Adapter,
Json11Array,
Json11ObjectMember,
Json11Object,
Json11Value>
{
public:
/// Construct a Json11Adapter that contains an empty object
Json11Adapter()
: BasicAdapter() { }
/// Construct a Json11Adapter containing a specific Json11 value
Json11Adapter(const json11::Json &value)
: BasicAdapter(value) { }
};
/**
* @brief Class for iterating over values held in a JSON array.
*
* This class provides a JSON array iterator that dereferences as an instance of
* Json11Adapter representing a value stored in the array. It has been
* implemented using the boost iterator_facade template.
*
* @see Json11Array
*/
class Json11ArrayValueIterator:
public boost::iterator_facade<
Json11ArrayValueIterator, // name of derived type
Json11Adapter, // value type
boost::bidirectional_traversal_tag, // bi-directional iterator
Json11Adapter> // type returned when dereferenced
{
public:
/**
* @brief Construct a new Json11ArrayValueIterator using an existing
* Json11 iterator.
*
* @param itr Json11 iterator to store
*/
Json11ArrayValueIterator(
const json11::Json::array::const_iterator &itr)
: itr(itr) { }
/// Returns a Json11Adapter that contains the value of the current
/// element.
Json11Adapter dereference() const
{
return Json11Adapter(*itr);
}
/**
* @brief Compare this iterator against another iterator.
*
* Note that this directly compares the iterators, not the underlying
* values, and assumes that two identical iterators will point to the same
* underlying object.
*
* @param other iterator to compare against
*
* @returns true if the iterators are equal, false otherwise.
*/
bool equal(const Json11ArrayValueIterator &other) const
{
return itr == other.itr;
}
void increment()
{
itr++;
}
void decrement()
{
itr--;
}
void advance(std::ptrdiff_t n)
{
itr += n;
}
private:
json11::Json::array::const_iterator itr;
};
/**
* @brief Class for iterating over the members belonging to a JSON object.
*
* This class provides a JSON object iterator that dereferences as an instance
* of Json11ObjectMember representing one of the members of the object. It
* has been implemented using the boost iterator_facade template.
*
* @see Json11Object
* @see Json11ObjectMember
*/
class Json11ObjectMemberIterator:
public boost::iterator_facade<
Json11ObjectMemberIterator, // name of derived type
Json11ObjectMember, // value type
boost::bidirectional_traversal_tag, // bi-directional iterator
Json11ObjectMember> // type returned when dereferenced
{
public:
/**
* @brief Construct an iterator from a Json11 iterator.
*
* @param itr Json11 iterator to store
*/
Json11ObjectMemberIterator(
const json11::Json::object::const_iterator &itr)
: itr(itr) { }
/**
* @brief Returns a Json11ObjectMember that contains the key and value
* belonging to the object member identified by the iterator.
*/
Json11ObjectMember dereference() const
{
return Json11ObjectMember(itr->first, itr->second);
}
/**
* @brief Compare this iterator with another iterator.
*
* Note that this directly compares the iterators, not the underlying
* values, and assumes that two identical iterators will point to the same
* underlying object.
*
* @param other Iterator to compare with
*
* @returns true if the underlying iterators are equal, false otherwise
*/
bool equal(const Json11ObjectMemberIterator &other) const
{
return itr == other.itr;
}
void increment()
{
itr++;
}
void decrement()
{
itr--;
}
private:
/// Iternal copy of the original Json11 iterator
json11::Json::object::const_iterator itr;
};
/// Specialisation of the AdapterTraits template struct for Json11Adapter.
template<>
struct AdapterTraits<valijson::adapters::Json11Adapter>
{
typedef json11::Json DocumentType;
static std::string adapterName()
{
return "Json11Adapter";
}
};
inline bool Json11FrozenValue::equalTo(const Adapter &other, bool strict) const
{
return Json11Adapter(value).equalTo(other, strict);
}
inline Json11ArrayValueIterator Json11Array::begin() const
{
const json11::Json::array &array = value.array_items();
return array.begin();
}
inline Json11ArrayValueIterator Json11Array::end() const
{
const json11::Json::array &array = value.array_items();
return array.end();
}
inline Json11ObjectMemberIterator Json11Object::begin() const
{
const json11::Json::object &object = value.object_items();
return object.begin();
}
inline Json11ObjectMemberIterator Json11Object::end() const
{
const json11::Json::object &object = value.object_items();
return object.end();
}
inline Json11ObjectMemberIterator Json11Object::find(
const std::string &propertyName) const
{
const json11::Json::object &object = value.object_items();
return object.find(propertyName);
}
} // namespace adapters
} // namespace valijson
#endif

View File

@ -0,0 +1,36 @@
#ifndef __VALIJSON_UTILS_JSON11_UTILS_HPP
#define __VALIJSON_UTILS_JSON11_UTILS_HPP
#include <json11.hpp>
#include <valijson/utils/file_utils.hpp>
namespace valijson {
namespace utils {
inline bool loadDocument(const std::string &path, json11::Json &document)
{
// Load schema JSON from file
std::string file;
if (!loadFile(path, file)) {
std::cerr << "Failed to load json from file '" << path << "'." << std::endl;
return false;
}
// Parse schema
std::string err;
document = json11::Json::parse(file, err);
if (!err.empty()) {
std::cerr << "json11 failed to parse the document:" << std::endl
<< "Parse error: " << err << std::endl;
return false;
}
return true;
}
} // namespace utils
} // namespace valijson
#endif

View File

@ -0,0 +1,85 @@
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <gtest/gtest.h>
#include <valijson/adapters/json11_adapter.hpp>
class TestJson11Adapter : public testing::Test
{
};
TEST_F(TestJson11Adapter, BasicArrayIteration)
{
const unsigned int numElements = 10;
// Create a Json11 document that consists of an array of numbers
json11::Json::array array;
for (unsigned int i = 0; i < numElements; i++) {
json11::Json value(static_cast<double>(i));
array.push_back(value);
}
json11::Json document(array);
// Ensure that wrapping the document preserves the array and does not allow
// it to be cast to other types
valijson::adapters::Json11Adapter adapter(document);
ASSERT_NO_THROW( adapter.getArray() );
ASSERT_ANY_THROW( adapter.getBool() );
ASSERT_ANY_THROW( adapter.getDouble() );
ASSERT_ANY_THROW( adapter.getObject() );
ASSERT_ANY_THROW( adapter.getString() );
// Ensure that the array contains the expected number of elements
EXPECT_EQ( numElements, adapter.getArray().size() );
// Ensure that the elements are returned in the order they were inserted
unsigned int expectedValue = 0;
BOOST_FOREACH( const valijson::adapters::Json11Adapter value, adapter.getArray() ) {
ASSERT_TRUE( value.isNumber() );
EXPECT_EQ( double(expectedValue), value.getDouble() );
expectedValue++;
}
// Ensure that the correct number of elements were iterated over
EXPECT_EQ(numElements, expectedValue);
}
TEST_F(TestJson11Adapter, BasicObjectIteration)
{
const unsigned int numElements = 10;
// Create a DropBoxJson11 document that consists of an object that maps numeric
// strings their corresponding numeric values
json11::Json::object object;
for (unsigned int i = 0; i < numElements; i++) {
std::string name(boost::lexical_cast<std::string>(i));
object[name] = json11::Json(static_cast<double>(i));
}
json11::Json document(object);
// Ensure that wrapping the document preserves the object and does not
// allow it to be cast to other types
valijson::adapters::Json11Adapter adapter(document);
ASSERT_NO_THROW( adapter.getObject() );
ASSERT_ANY_THROW( adapter.getArray() );
ASSERT_ANY_THROW( adapter.getBool() );
ASSERT_ANY_THROW( adapter.getDouble() );
ASSERT_ANY_THROW( adapter.getString() );
// Ensure that the object contains the expected number of members
EXPECT_EQ( numElements, adapter.getObject().size() );
// Ensure that the members are returned in the order they were inserted
unsigned int expectedValue = 0;
BOOST_FOREACH( const valijson::adapters::Json11Adapter::ObjectMember member, adapter.getObject() ) {
ASSERT_TRUE( member.second.isNumber() );
EXPECT_EQ( boost::lexical_cast<std::string>(expectedValue), member.first );
EXPECT_EQ( double(expectedValue), member.second.getDouble() );
expectedValue++;
}
// Ensure that the correct number of elements were iterated over
EXPECT_EQ( numElements, expectedValue );
}