diff --git a/inc/LauJsonTools.hh b/inc/LauJsonTools.hh new file mode 100644 index 0000000..4b92eba --- /dev/null +++ b/inc/LauJsonTools.hh @@ -0,0 +1,150 @@ + +/* +Copyright 2023 University of Warwick + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +Laura++ package authors: +John Back +Paul Harrison +Thomas Latham +*/ + +#ifndef LAU_JSON_TOOLS_HH +#define LAU_JSON_TOOLS_HH + +#include + +#include +#include + +/*! \file LauJsonTools.hh + \brief File containing LauJsonTools namespace +*/ + +/*! \namespace LauJsonTools + \brief Namespace containing tools for reading/writing JSON files +*/ + +namespace LauJsonTools { + + /*! \enum JsonType + \brief Define types that can be found in JSON fields and that we can handle + */ + enum class JsonType { + Null, //!< JSON value is null + Object, //!< JSON value is an object + Array, //!< JSON value is an array + String, //!< JSON value is a string + Boolean, //!< JSON value is a boolean + Number_Integer, //!< JSON value is an integer (signed or unsigned) + Number_Unsigned, //!< JSON value is an unsigned integer + Number_Float, //!< JSON value is a floating point value + Number, //!< JSON value is any number (integer, unsigned or float) + Primitive, //!< JSON value is any primative type (null, string, boolean, integer, unsigned, float) + Structured, //!< JSON value is any structured type (object, array) + Any //!< JSON value is any of the above + }; + + //! Typedef to define a combination of a JSON element's name and type + using ElementNameType = std::pair< std::string, JsonType >; + + //! Exception object to be thrown in case of a missing element + class MissingJsonElement : public std::runtime_error { + public: + //! Constructor + /*! + \param [in] what the message explaining the precise error that has occurred + */ + MissingJsonElement(const std::string& what) : std::runtime_error(what) {} + }; + + //! Deserialise a JSON file to a JSON value + /*! + \param [in] fileName the name of the file to read + \param [in] elementName the optional name of the element within the root object to retrieve - by default retrieve the root structure + \param [in] expectedType the expected type of the value + */ + nlohmann::json readJsonFile(const std::string& fileName, const std::string& elementName = "", const JsonType expectedType = JsonType::Any); + + //! Serialise a JSON value to a JSON file + /*! + \param [in] fileName the name of the file to be written + \param [in] value the JSON value to serialise + \param [in] indent the indentation level to use in the output in number of spaces (defaults to 4)\n + From the nlohmann::json::dump documentation:\n + If indent is nonnegative, then array elements and object members will be pretty-printed with that indent level. + An indent level of 0 will only insert newlines. + A value of -1 selects the most compact representation. + \return true if file successfully written, false otherwise + */ + bool writeJsonFile(const std::string& fileName, const nlohmann::json& value, const int indent = 4); + + //! Check that the type of a JSON value is as expected + /*! + \param [in] value the JSON value to check + \param [in] expectedType the expected type of the value + */ + bool checkValueType(const nlohmann::json& value, const JsonType expectedType); + + //! Check that the expected JSON elements are present in the supplied object + /*! + \param [in] obj the JSON object to check + \param [in] expectedElements the elements (names and types) that are expected + \return true if all expected elements are present, false otherwise + */ + bool checkObjectElements( const nlohmann::json& obj, const std::vector& expectedElements ); + + //! Access the value of an element that should be present in an object (no checking is done) + /*! + \param [in] obj the JSON object to access + \param [in] elementName the name of the element within the object to retrieve + \return the value of the element + */ + template + T getValue( const nlohmann::json& obj, const std::string& elementName ) { return obj.at( elementName ).get(); } + + //! Access an element that may or may not be present in an object + /*! + \param [in] obj the JSON object to access + \param [in] elementName the name of the element within the object to retrieve + \param [in] expectedType the expected type of the element + \return a pointer to the element, or nullptr if it is not present + */ + const nlohmann::json* getOptionalElement( const nlohmann::json& obj, const std::string& elementName, const JsonType expectedType = JsonType::Any ); + + //! Access the value of an element that may or may not be present in an object + /*! + \param [in] obj the JSON object to access + \param [in] elementName the name of the element within the object to retrieve + \param [in] expectedType the expected type of the element + \return the value of the element as a std::optional + */ + template + std::optional getOptionalValue( const nlohmann::json& obj, const std::string& elementName, const JsonType expectedType = JsonType::Any ); +} + +template +std::optional LauJsonTools::getOptionalValue( const nlohmann::json& obj, const std::string& elementName, const JsonType expectedType ) +{ + auto elem { getOptionalElement( obj, elementName, expectedType ) }; + if ( ! elem ) { + return {}; + } + + return elem->get(); +} + +#endif diff --git a/inc/Laura++_LinkDef.h b/inc/Laura++_LinkDef.h index de4bbc7..bd91d56 100644 --- a/inc/Laura++_LinkDef.h +++ b/inc/Laura++_LinkDef.h @@ -1,187 +1,188 @@ /* Copyright 2013 University of Warwick Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Laura++ package authors: John Back Paul Harrison Thomas Latham */ #ifdef __CINT__ #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class Lau1DCubicSpline+; #pragma link C++ class Lau1DHistPdf+; #pragma link C++ class Lau2DAbsDP+; #pragma link C++ class Lau2DAbsDPPdf+; #pragma link C++ class Lau2DAbsHistDP+; #pragma link C++ class Lau2DAbsHistDPPdf+; #pragma link C++ class Lau2DCubicSpline+; #pragma link C++ class Lau2DHistDP+; #pragma link C++ class Lau2DHistDPPdf+; #pragma link C++ class Lau2DHistPdf+; #pragma link C++ class Lau2DSplineDP+; #pragma link C++ class Lau2DSplineDPPdf+; #pragma link C++ class LauAbsBkgndDPModel+; #pragma link C++ class LauAbsCoeffSet+; #pragma link C++ class LauAbsEffModel+; #pragma link C++ class LauAbsFitter+; #pragma link C++ class LauAbsFitModel+; #pragma link C++ class LauAbsIncohRes+; #pragma link C++ class LauAbsModIndPartWave+; #pragma link C++ class LauAbsPdf+; #pragma link C++ class LauAbsResonance+; #pragma link C++ class LauAbsRValue+; #pragma link C++ class LauArgusPdf+; #pragma link C++ class LauAsymmCalc+; #pragma link C++ class LauBelleCPCoeffSet+; #pragma link C++ class LauBelleNR+; #pragma link C++ class LauBelleSymNR+; #pragma link C++ class LauBifurcatedGaussPdf+; #pragma link C++ class LauBkgndDPModel+; #pragma link C++ class LauBlattWeisskopfFactor+; #pragma link C++ class LauBlind+; #pragma link C++ class LauBreitWignerRes+; #pragma link C++ class LauBsCPFitModel+; #pragma link C++ class LauBuggRes+; #pragma link C++ class LauCacheData+; #pragma link C++ class LauCalcChiSq+; #pragma link C++ class LauCartesianCPCoeffSet+; #pragma link C++ class LauCartesianGammaCPCoeffSet+; #pragma link C++ class LauCategoryFlavTag+; #pragma link C++ class LauChebychevPdf+; #pragma link C++ class LauCleoCPCoeffSet+; #pragma link C++ class LauComplex+; #pragma link C++ class LauCPFitModel+; #pragma link C++ class LauCruijffPdf+; #pragma link C++ class LauCrystalBallPdf+; #pragma link C++ class LauDabbaRes+; #pragma link C++ class LauDatabasePDG+; #pragma link C++ class LauDaughters+; #pragma link C++ class LauDecayTimePdf+; #pragma link C++ class LauDPDepBifurGaussPdf+; #pragma link C++ class LauDPDepCruijffPdf+; #pragma link C++ class LauDPDepGaussPdf+; #pragma link C++ class LauDPDepMapPdf+; #pragma link C++ class LauDPDepSumPdf+; #pragma link C++ class LauEffModel+; #pragma link C++ class LauEFKLLMRes+; #pragma link C++ class LauEmbeddedData+; #pragma link C++ class LauExponentialPdf+; #pragma link C++ class LauFitDataTree+; #pragma link C++ class LauFitNtuple+; #pragma link C++ class LauFitter+; #pragma link C++ class LauFitObject+; #pragma link C++ class LauFlatteRes+; #pragma link C++ class LauFlatNR+; #pragma link C++ class LauFlavTag+; #pragma link C++ class LauFormulaPar+; #pragma link C++ class LauGaussIncohRes+; #pragma link C++ class LauGaussPdf+; #pragma link C++ class LauGenNtuple+; #pragma link C++ class LauGounarisSakuraiRes+; #pragma link C++ class LauIntegrals+; #pragma link C++ class LauDPPartialIntegralInfo+; #pragma link C++ class LauIsobarDynamics+; #pragma link C++ class LauKappaRes+; #pragma link C++ class LauKinematics+; #pragma link C++ class LauKMatrixProdPole+; #pragma link C++ class LauKMatrixProdSVP+; #pragma link C++ class LauKMatrixPropagator+; #pragma link C++ class LauKMatrixPropFactory+; #pragma link C++ class LauLASSBWRes+; #pragma link C++ class LauLASSNRRes+; #pragma link C++ class LauLASSRes+; #pragma link C++ class LauLinearPdf+; #pragma link C++ class LauLHCbNR+; #pragma link C++ class LauMagPhaseCoeffSet+; #pragma link C++ class LauMagPhaseCPCoeffSet+; #pragma link C++ class LauMergeDataFiles+; #pragma link C++ class LauMinuit+; #pragma link C++ class LauModIndPartWaveMagPhase+; #pragma link C++ class LauModIndPartWaveRealImag+; #pragma link C++ class LauNovosibirskPdf+; #pragma link C++ class LauNRAmplitude+; #pragma link C++ class LauNSCCartesianCPCoeffSet+; #pragma link C++ class LauParameter+; #pragma link C++ class LauParametricStepFuncPdf+; #pragma link C++ class LauParamFixed+; #pragma link C++ class LauParticlePDG+; #pragma link C++ class LauPolNR+; #pragma link C++ class LauPoleRes+; #pragma link C++ class LauPolarFormFactorNR+; #pragma link C++ class LauPolarFormFactorSymNR+; #pragma link C++ class LauPolarGammaCPCoeffSet+; #pragma link C++ class LauPrint+; #pragma link C++ class LauRealImagCoeffSet+; #pragma link C++ class LauRealImagCPCoeffSet+; #pragma link C++ class LauRealImagGammaCPCoeffSet+; #pragma link C++ class LauRelBreitWignerRes+; #pragma link C++ class LauResonanceInfo+; #pragma link C++ class LauRescatteringRes+; #pragma link C++ class LauRescattering2Res+; #pragma link C++ class LauResonanceMaker+; #pragma link C++ class LauResultsExtractor+; #pragma link C++ class LauRhoOmegaMix+; #ifdef DOLAUROOFITTASK #pragma link C++ class LauRooFitTask+; #endif #pragma link C++ class LauScfMap+; #pragma link C++ class LauSigmaRes+; #pragma link C++ class LauSigmoidPdf+; #pragma link C++ class LauSimpleFitModel+; #pragma link C++ class LauSimFitCoordinator+; #pragma link C++ class LauSimFitTask+; #pragma link C++ class LauSPlot+; #pragma link C++ class LauString+; #pragma link C++ class LauSumPdf+; #pragma link C++ class LauTextFileParser+; #pragma link C++ class LauTimeDepFlavModel+; #pragma link C++ class LauTimeDepFitModel+; #pragma link C++ class LauTimeDepNonFlavModel+; #pragma link C++ class LauVetoes+; #pragma link C++ class LauWeightedSumEffModel+; #pragma link C++ class LauAbsDecayTimeCalculator+; #pragma link C++ class LauAbsDecayTimeEfficiency+; #pragma link C++ class LauAbsDecayTimeIntegrator+; #pragma link C++ class LauBinnedDecayTimeEfficiency+; #pragma link C++ class LauDecayTimePhysicsModel+; #pragma link C++ class LauDecayTimeResolution+; #pragma link C++ class LauNonSmearedDecayTimeCalculator+; #pragma link C++ class LauNonSmearedBinnedEfficiencyDecayTimeIntegrator+; #pragma link C++ class LauNonSmearedSplineEfficiencyDecayTimeIntegrator+; #pragma link C++ class LauNonSmearedSplineEfficiencyDecayTimeIntegrator+; #pragma link C++ class LauNonSmearedUniformEfficiencyDecayTimeIntegrator+; #pragma link C++ class LauSmearedDecayTimeCalculator+; #pragma link C++ class LauSmearedBinnedEfficiencyDecayTimeIntegrator+; #pragma link C++ class LauSmearedSplineEfficiencyDecayTimeIntegrator+; #pragma link C++ class LauSmearedSplineEfficiencyDecayTimeIntegrator+; #pragma link C++ class LauSmearedUniformEfficiencyDecayTimeIntegrator+; #pragma link C++ class LauSplineDecayTimeEfficiency+; #pragma link C++ class LauSplineDecayTimeEfficiency+; #pragma link C++ class LauUniformDecayTimeEfficiency+; #pragma link C++ namespace LauConstants+; #pragma link C++ namespace LauDecayTime+; +#pragma link C++ namespace LauJsonTools+; #pragma link C++ namespace LauRandom+; #endif diff --git a/src/LauJsonTools.cc b/src/LauJsonTools.cc new file mode 100644 index 0000000..f2d9e65 --- /dev/null +++ b/src/LauJsonTools.cc @@ -0,0 +1,160 @@ + +/* +Copyright 2023 University of Warwick + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +Laura++ package authors: +John Back +Paul Harrison +Thomas Latham +*/ + +/*! \file LauJsonTools.cc + \brief File containing implementation of functions in the LauJsonTools namespace +*/ + +#include "LauJsonTools.hh" + +#include + +#include +#include +#include + +nlohmann::json LauJsonTools::readJsonFile(const std::string& fileName, const std::string& elementName, const JsonType expectedType) +{ + // load the JSON from the file + std::ifstream in{ fileName }; + if ( ! in ) { + std::cerr << "ERROR in LauJsonTools::readJsonFile : unable to open file \"" << fileName << "\"" << std::endl; + return {}; + } + + nlohmann::json j { nlohmann::json::parse(in) }; + + // optionally retrieve a particular element, otherwise just stick with the root structure + if ( ! elementName.empty() ) { + if ( ! j.is_object() ) { + std::cerr << "ERROR in LauJsonTools::readJsonFile : root structure in JSON file \"" << fileName << "\" is not an object, so cannot retrieve the specified element \"" << elementName << "\"" << std::endl; + return {}; + } + + if ( ! j.contains( elementName ) ) { + std::cerr << "ERROR in LauJsonTools::readJsonFile : root object in JSON file \"" << fileName << "\" does not contain specified element \"" << elementName << "\"" << std::endl; + return {}; + } + + j = j.at( elementName ); + } + + // check type is as expected + if ( ! checkValueType( j, expectedType ) ) { + if ( ! elementName.empty() ) { + std::cerr << "ERROR in LauJsonTools::readJsonFile : element \"" << elementName << "\" of JSON file \"" << fileName << "\" is not expected type" << std::endl; + } else { + std::cerr << "ERROR in LauJsonTools::readJsonFile : root element of JSON file \"" << fileName << "\" is not expected type" << std::endl; + } + return {}; + } + + return j; +} + +bool LauJsonTools::writeJsonFile(const std::string& fileName, const nlohmann::json& value, const int indent) +{ + std::ofstream out{fileName, std::ios_base::out}; + if ( not out ) { + std::cerr << "ERROR in LauJsonTools::writeJsonFile : couldn't open file \"" << fileName << "\" for writing. No file will be written!" << std::endl; + return false; + } + + out << value.dump(indent); + out << std::endl; + out.close(); + + return true; +} + +bool LauJsonTools::checkValueType(const nlohmann::json& value, const JsonType expectedType) +{ + switch ( expectedType ) { + case JsonType::Null : + return value.is_null(); + case JsonType::Object : + return value.is_object(); + case JsonType::Array : + return value.is_array(); + case JsonType::String : + return value.is_string(); + case JsonType::Boolean : + return value.is_boolean(); + case JsonType::Number_Integer : + return value.is_number_integer(); + case JsonType::Number_Unsigned : + return value.is_number_unsigned(); + case JsonType::Number_Float : + return value.is_number_float(); + case JsonType::Number : + return value.is_number(); + case JsonType::Primitive : + return value.is_primitive(); + case JsonType::Structured : + return value.is_structured(); + case JsonType::Any : + return true; + } + + return false; +} + +bool LauJsonTools::checkObjectElements( const nlohmann::json& j, const std::vector& expectedElements ) +{ + if ( ! j.is_object() ) { + std::cerr << "ERROR in LauJsonTools::checkObjectElements : supplied JSON value is not an object" << std::endl; + } + + bool allElementsOK{ true }; + + for ( const auto& [ name, type ] : expectedElements ) { + if ( ! j.contains( name ) ) { + std::cerr << "ERROR in LauJsonTools::checkObjectElements : JSON object does not contain required element: " << name << std::endl; + allElementsOK = false; + continue; + } else if ( ! checkValueType( j.at( name ), type ) ) { + std::cerr << "ERROR in LauJsonTools::checkObjectElements : JSON object does not contain required type for element: " << name << std::endl; + allElementsOK = false; + continue; + } + } + + return allElementsOK; +} + +const nlohmann::json* LauJsonTools::getOptionalElement( const nlohmann::json& obj, const std::string& elementName, const JsonType expectedType ) +{ + if ( ! obj.contains( elementName ) ) { + return nullptr; + } + + const nlohmann::json& elem { obj.at( elementName ) }; + + if ( ! checkValueType( elem, expectedType ) ) { + std::cerr << "WARNING in LauJsonTools::getOptionalElement : element \"" << elementName << "\" exists within the supplied object but it is not of the expected type - therefore it will not be returned" << std::endl; + return nullptr; + } + + return &elem; +}