Skip to content
Snippets Groups Projects
Commit 94e96d3e authored by Gigg, Martyn Anthony's avatar Gigg, Martyn Anthony
Browse files

Add PropertyWithValueJSONDecoder type

It is able to decode a known set of types from JSON
to the appropriate C++ type.

Refs #23988
parent 09d6bea8
No related branches found
No related tags found
No related merge requests found
...@@ -90,6 +90,7 @@ set ( SRC_FILES ...@@ -90,6 +90,7 @@ set ( SRC_FILES
src/PropertyManagerProperty.cpp src/PropertyManagerProperty.cpp
src/PropertyNexus.cpp src/PropertyNexus.cpp
src/PropertyWithValue.cpp src/PropertyWithValue.cpp
src/PropertyWithValueJSONDecoder.cpp
src/ProxyInfo.cpp src/ProxyInfo.cpp
src/PseudoRandomNumberGenerator.cpp src/PseudoRandomNumberGenerator.cpp
src/Quat.cpp src/Quat.cpp
...@@ -163,7 +164,7 @@ set ( INC_FILES ...@@ -163,7 +164,7 @@ set ( INC_FILES
inc/MantidKernel/ConfigService.h inc/MantidKernel/ConfigService.h
inc/MantidKernel/ConfigObserver.h inc/MantidKernel/ConfigObserver.h
inc/MantidKernel/ConfigPropertyObserver.h inc/MantidKernel/ConfigPropertyObserver.h
inc/MantidKernel/DateAndTime.h inc/MantidKernel/DateAndTime.h
inc/MantidKernel/DataItem.h inc/MantidKernel/DataItem.h
inc/MantidKernel/DataService.h inc/MantidKernel/DataService.h
inc/MantidKernel/DateAndTimeHelpers.h inc/MantidKernel/DateAndTimeHelpers.h
...@@ -242,7 +243,7 @@ set ( INC_FILES ...@@ -242,7 +243,7 @@ set ( INC_FILES
inc/MantidKernel/NetworkProxy.h inc/MantidKernel/NetworkProxy.h
inc/MantidKernel/NeutronAtom.h inc/MantidKernel/NeutronAtom.h
inc/MantidKernel/NexusDescriptor.h inc/MantidKernel/NexusDescriptor.h
inc/MantidKernel/normal_distribution.h inc/MantidKernel/normal_distribution.h
inc/MantidKernel/NullValidator.h inc/MantidKernel/NullValidator.h
inc/MantidKernel/OptionalBool.h inc/MantidKernel/OptionalBool.h
inc/MantidKernel/ParaViewVersion.h inc/MantidKernel/ParaViewVersion.h
...@@ -260,6 +261,7 @@ set ( INC_FILES ...@@ -260,6 +261,7 @@ set ( INC_FILES
inc/MantidKernel/PropertyManager_fwd.h inc/MantidKernel/PropertyManager_fwd.h
inc/MantidKernel/PropertyNexus.h inc/MantidKernel/PropertyNexus.h
inc/MantidKernel/PropertyWithValue.h inc/MantidKernel/PropertyWithValue.h
inc/MantidKernel/PropertyWithValueJSONDecoder
inc/MantidKernel/ProxyInfo.h inc/MantidKernel/ProxyInfo.h
inc/MantidKernel/PseudoRandomNumberGenerator.h inc/MantidKernel/PseudoRandomNumberGenerator.h
inc/MantidKernel/QuasiRandomNumberSequence.h inc/MantidKernel/QuasiRandomNumberSequence.h
...@@ -410,6 +412,7 @@ set ( TEST_FILES ...@@ -410,6 +412,7 @@ set ( TEST_FILES
PropertyNexusTest.h PropertyNexusTest.h
PropertyTest.h PropertyTest.h
PropertyWithValueTest.h PropertyWithValueTest.h
PropertyWithValueJSONDecoderTest.h
ProxyInfoTest.h ProxyInfoTest.h
QuatTest.h QuatTest.h
ReadLockTest.h ReadLockTest.h
......
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2007 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source
// & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
#ifndef PROPERTYWITHVALUEJSONDECODER_H
#define PROPERTYWITHVALUEJSONDECODER_H
#include "MantidKernel/DllConfig.h"
#include <memory>
namespace Json {
class Value;
}
namespace Mantid {
namespace Kernel {
class Property;
/// Attempt to create a Property from a Json value object
MANTID_KERNEL_DLL std::unique_ptr<Property> decode(const Json::Value &value);
} // namespace Kernel
} // namespace Mantid
#endif // PROPERTYWITHVALUEJSONDECODER_H
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright &copy; 2007 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source
// & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidKernel/PropertyWithValueJSONDecoder.h"
#include "MantidKernel/PropertyWithValue.h"
#include <json/json.h>
#include <unordered_map>
using Json::StreamWriterBuilder;
using Json::Value;
using Json::writeString;
namespace Mantid {
namespace Kernel {
namespace {
// Define a lookup mapping Json::ValueTypes to functions able to
// create a PropertyWithValue object from that type
using FromJsonFn = std::function<std::unique_ptr<Property>(
const std::string &, const Json::Value &)>;
using FromJsonConverters = std::map<Json::ValueType, FromJsonFn>;
// A pointer to a member function for doing the Json::Value->C++ type conversion
template <typename T> using ValueAsTypeMemFn = T (Json::Value::*)() const;
// A generic function accepting a pointer to a Json::Value::as* function, e.g.
// asInt, plus the name and value of the property
template <typename ValueType>
std::unique_ptr<Property> fromJson(ValueAsTypeMemFn<ValueType> asFn,
const std::string &name,
const Json::Value &value) {
return std::make_unique<PropertyWithValue<ValueType>>(name,
(value.*(asFn))());
}
// Returns (and creates on first call) the map of Json::ValueType to
// FromJsonFn for converting a JsonValue to a Property object
const FromJsonConverters &converters() {
static FromJsonConverters converters;
if (converters.empty()) {
using namespace std::placeholders;
// Build a map of Json types to fromJson functions of the appropriate type
converters.insert(std::make_pair(
Json::booleanValue,
std::bind(fromJson<bool>, &Json::Value::asBool, _1, _2)));
converters.insert(std::make_pair(
Json::intValue, std::bind(fromJson<int>, &Json::Value::asInt, _1, _2)));
converters.insert(std::make_pair(
Json::realValue,
std::bind(fromJson<double>, &Json::Value::asDouble, _1, _2)));
converters.insert(std::make_pair(
Json::stringValue,
std::bind(fromJson<std::string>, &Json::Value::asString, _1, _2)));
}
return converters;
}
/**
* @brief Create a PropertyWithValue object from the given Json::Value
* @param name The name of the new property
* @param value The value as Json::Value
* @return A pointer to a new Property object
* @throws std::invalid_argument if the type of the Json::Value is not known
*/
std::unique_ptr<Property> createProperty(const std::string &name,
const Json::Value &value) {
auto conversionFnIter = converters().find(value.type());
if (conversionFnIter == converters().end()) {
throw std::invalid_argument(
"Cannot create property with name " + name +
". Unable to find converter for Json::ValueType to C++ type");
}
return conversionFnIter->second(name, value);
}
} // namespace
/**
* @brief decode
* @param value A value as a Json serialized quantity
* @return
*/
std::unique_ptr<Property> decode(const Json::Value &value) {
if (value.size() != 1) {
StreamWriterBuilder wbuilder;
throw std::invalid_argument(
"Expected Json::Value with a single member. Found " +
writeString(wbuilder, value));
}
Value::Members members(value.getMemberNames());
assert(members.size() == 1);
return createProperty(members[0], value[members.front()]);
}
} // namespace Kernel
} // namespace Mantid
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright &copy; 2007 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source
// & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
#ifndef PROPERTYWITHVALUEJSONDECODERTEST_H
#define PROPERTYWITHVALUEJSONDECODERTEST_H
#include <cxxtest/TestSuite.h>
#include <jsoncpp/json/value.h>
#include "MantidKernel/PropertyWithValue.h"
#include "MantidKernel/PropertyWithValueJSONDecoder.h"
class PropertyWithValueJSONDecoderTest : public CxxTest::TestSuite {
public:
static PropertyWithValueJSONDecoderTest *createSuite() {
return new PropertyWithValueJSONDecoderTest;
}
static void destroySuite(PropertyWithValueJSONDecoderTest *suite) {
return delete suite;
}
void testDecodeSingleJSONIntAsProperty() {
doSimpleObjectDecodeTest("IntProperty", 10);
}
void testDecodeSingleJSONDoubleAsProperty() {
doSimpleObjectDecodeTest("DoubleProperty", 10.5);
}
void testDecodeSingleJSONStringAsProperty() {
doSimpleObjectDecodeTest("StringProperty", std::string("My value"));
}
void testDecodeSingleJSONBoolAsProperty() {
doSimpleObjectDecodeTest("BoolProperty", false);
}
// ----------------------- Failure tests -----------------------
void testDecodeThrowsWithEmptyValue() {
using Mantid::Kernel::decode;
Json::Value root;
TSM_ASSERT_THROWS("Expected decode to throw for empty value", decode(root),
std::invalid_argument);
}
void testDecodeThrowsWithGreaterThanOneMember() {
using Mantid::Kernel::decode;
Json::Value root;
root["one"] = 1;
root["two"] = 2;
TSM_ASSERT_THROWS("Expected decode to throw with more than 1 member",
decode(root), std::invalid_argument);
}
void testDecodeThrowsWithNonObjectValue() {
using Mantid::Kernel::decode;
TSM_ASSERT_THROWS("Expected decode to throw with non-object type",
decode(Json::Value(10)), std::invalid_argument);
}
private:
template <typename ValueType>
void doSimpleObjectDecodeTest(const std::string &propName,
const ValueType &propValue) {
Json::Value root;
root[propName] = propValue;
using Mantid::Kernel::decode;
auto property = decode(root);
TSM_ASSERT("Decode failed to create a Property. ", property);
using Mantid::Kernel::PropertyWithValue;
auto typedProperty =
dynamic_cast<PropertyWithValue<ValueType> *>(property.get());
TSM_ASSERT("Property has unexpected type ", typedProperty);
TS_ASSERT_EQUALS(propName, typedProperty->name());
TS_ASSERT_EQUALS(propValue, (*typedProperty)());
}
};
#endif // PROPERTYWITHVALUEJSONDECODERTEST_H
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment