diff --git a/Det/LbDD4hep/CMakeLists.txt b/Det/LbDD4hep/CMakeLists.txt index 5afb19239c9bb0c2ce2196edc9b82feeb371a59b..f61ead21aa87b59723a54df010769fd78b0889f9 100644 --- a/Det/LbDD4hep/CMakeLists.txt +++ b/Det/LbDD4hep/CMakeLists.txt @@ -22,6 +22,8 @@ if(USE_DD4HEP) Gaudi::GaudiAlgLib Gaudi::GaudiKernel LHCb::LHCbKernel + nlohmann_json::nlohmann_json + yaml-cpp ) if(DD4hep_ROOT) diff --git a/Det/LbDD4hep/include/LbDD4hep/ConditionAccessorHolder.h b/Det/LbDD4hep/include/LbDD4hep/ConditionAccessorHolder.h index 946d4648d4fe6d4999a4c0da3e3481c8194f419e..85b806b4ff62173e070e301e2c8899c2dc89ef64 100644 --- a/Det/LbDD4hep/include/LbDD4hep/ConditionAccessorHolder.h +++ b/Det/LbDD4hep/include/LbDD4hep/ConditionAccessorHolder.h @@ -21,6 +21,7 @@ #include <GaudiKernel/detected.h> #include <Kernel/STLExtensions.h> #include <LbDD4hep/IDD4hepSvc.h> +#include <LbDD4hep/utils.h> #include <any> #include <stdexcept> #include <type_traits> @@ -139,7 +140,9 @@ namespace LHCb::Det::LbDD4hep { return T( cond ); } else { using DT = std::decay_t<T>; - if constexpr ( !detail::PassAsAny<DT>::value ) { + if constexpr ( std::is_same_v<DT, nlohmann::json> ) { + return utils::y2j( cond.get<YAML::Node>() ); + } else if constexpr ( !detail::PassAsAny<DT>::value ) { return cond.get<DT>(); } else { const DT* p = nullptr; diff --git a/Det/LbDD4hep/include/LbDD4hep/IDD4hepSvc.h b/Det/LbDD4hep/include/LbDD4hep/IDD4hepSvc.h index 6186c49d2a312b3cbc20f776ebcec1cf59702ab6..934a8fb3bd4b1273b0717c402a60d5e0e43b4086 100644 --- a/Det/LbDD4hep/include/LbDD4hep/IDD4hepSvc.h +++ b/Det/LbDD4hep/include/LbDD4hep/IDD4hepSvc.h @@ -20,6 +20,7 @@ #include <DDCond/ConditionsSlice.h> #include <GaudiKernel/IService.h> #include <Kernel/STLExtensions.h> +#include <LbDD4hep/utils.h> #include <any> #include <boost/callable_traits.hpp> #include <cstdint> @@ -126,7 +127,9 @@ namespace LHCb::Det::LbDD4hep { return Input( cond ); } else { using DT = std::decay_t<Input>; - if constexpr ( !PassAsAny<DT>::value ) { + if constexpr ( std::is_same_v<DT, nlohmann::json> ) { + return utils::y2j( cond.get<YAML::Node>() ); + } else if constexpr ( !PassAsAny<DT>::value ) { return cond.get<DT>(); } else { const DT* p = nullptr; diff --git a/Det/LbDD4hep/include/LbDD4hep/utils.h b/Det/LbDD4hep/include/LbDD4hep/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..06c2fb01b05ba59c7dd2a408a8354615435287c1 --- /dev/null +++ b/Det/LbDD4hep/include/LbDD4hep/utils.h @@ -0,0 +1,72 @@ +/*****************************************************************************\ +* (c) Copyright 2022 CERN for the benefit of the LHCb Collaboration * +* * +* This software is distributed under the terms of the GNU General Public * +* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". * +* * +* In applying this licence, CERN does not waive the privileges and immunities * +* granted to it by virtue of its status as an Intergovernmental Organization * +* or submit itself to any jurisdiction. * +\*****************************************************************************/ +#pragma once + +#include <algorithm> +#include <cctype> +#include <nlohmann/json.hpp> +#include <string> +#include <yaml-cpp/yaml.h> + +namespace LHCb::Det::LbDD4hep::utils { + /// Convert a YAML::Node instance to a nlohmann::json object. + /// + /// Since yaml-cpp does not make any assumption about [YAML untagged + /// nodes](https://yaml.org/spec/1.2.2/#resolved-tags) leaving it to the application to decide how to interprete the + /// values, this conversion function tries to map the value to bool, int64_t or double on a best effort basis, falling + /// back on std::string in case of failure. + inline nlohmann::json y2j( const YAML::Node& y ) { + nlohmann::json j; + switch ( y.Type() ) { + case YAML::NodeType::Sequence: { + j = nlohmann::json::array(); + for ( const auto& element : y ) { j.push_back( y2j( element ) ); } + } break; + case YAML::NodeType::Map: { + j = nlohmann::json::object(); + for ( const auto& entry : y ) { j[entry.first.as<std::string>()] = y2j( entry.second ); } + } break; + case YAML::NodeType::Scalar: { + auto tmp = y.as<std::string>(); + j = tmp; // implicit default: fall back on a string + if ( tmp.empty() ) { + // just an empty string is OK + } else if ( tmp == "true" ) { + j = true; + } else if ( tmp == "false" ) { + j = false; + } else { // if it's not a boolean literal, we try with numbers + try { + j = y.as<std::int64_t>(); + } catch ( YAML::BadConversion& ) { + try { + j = y.as<double>(); + } catch ( YAML::BadConversion& ) { + // not an int, not a float, we can only fall back on string + } + } + } + } break; + case YAML::NodeType::Undefined: + case YAML::NodeType::Null: + default: { + // nothing to do in these cases + } + } + return j; + } + + /// Convert a nlohmann::json object to a YAML::Node instance. + /// + /// This function relies on YAML syntax being a superset of JSON, so we serialize + /// and deserialize. + inline YAML::Node j2y( const nlohmann::json& j ) { return YAML::Load( j.dump() ); } +} // namespace LHCb::Det::LbDD4hep::utils