blob: f6b9b1c9fa3f621b6ee80e5d87cef2dacf523e04 [file] [log] [blame]
/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
#pragma once
#include <string>
#include <utility>
#include <memory>
#include "core/flow/Node.h"
#include "rapidjson/document.h"
#include "utils/gsl.h"
#include "utils/ValueCaster.h"
namespace org::apache::nifi::minifi::core {
class JsonNode : public flow::Node::NodeImpl {
public:
explicit JsonNode(const rapidjson::Value* node): node_(node) {}
explicit operator bool() const override {
return node_ != nullptr;
}
bool isSequence() const override {
return node_ ? node_->IsArray() : false;
}
bool isMap() const override {
return node_ ? node_->IsObject() : false;
}
bool isNull() const override {
return node_ ? node_->IsNull() : false;
}
flow::Node createEmpty() const override {
return flow::Node{std::make_shared<JsonNode>(nullptr)};
}
nonstd::expected<std::string, std::exception_ptr> getString() const override {
try {
if (!node_) {
throw std::runtime_error(fmt::format("Cannot get string of invalid json value at '{}'", path_));
}
if (!node_->IsString()) {
throw std::runtime_error(fmt::format("Cannot get string of non-string json value at '{}'", path_));
}
return std::string{node_->GetString(), node_->GetStringLength()};
} catch (...) {
return nonstd::make_unexpected(std::current_exception());
}
}
nonstd::expected<int64_t, std::exception_ptr> getInt64() const override {
try {
if (!node_) {
throw std::runtime_error(fmt::format("Cannot get int64 of invalid json value at '{}'", path_));
}
if (!node_->IsInt64()) {
throw std::runtime_error(fmt::format("Cannot get int64 of non-int64 json value at '{}'", path_));
}
return node_->GetInt64();
} catch (...) {
return nonstd::make_unexpected(std::current_exception());
}
}
nonstd::expected<bool, std::exception_ptr> getBool() const override {
try {
if (!node_) {
throw std::runtime_error(fmt::format("Cannot get bool of invalid json value at '{}'", path_));
}
if (!node_->IsBool()) {
throw std::runtime_error(fmt::format("Cannot get bool of non-bool json value at '{}'", path_));
}
return node_->GetBool();
} catch (...) {
return nonstd::make_unexpected(std::current_exception());
}
}
nonstd::expected<std::string, std::exception_ptr> getIntegerAsString() const override {
try {
if (!node_) throw std::runtime_error(fmt::format("Cannot get string from invalid json value at '{}'", path_));
if (node_->IsInt64()) return std::to_string(node_->GetInt64());
if (node_->IsUint64()) return std::to_string(node_->GetUint64());
if (node_->IsString()) return std::string(node_->GetString(), node_->GetStringLength());
throw std::runtime_error(fmt::format("Cannot get string from non-integer json value at '{}'", path_));
} catch (...) {
return nonstd::make_unexpected(std::current_exception());
}
}
nonstd::expected<std::string, std::exception_ptr> getScalarAsString() const override {
try {
if (!node_) throw std::runtime_error(fmt::format("Cannot get string from invalid json value at '{}'", path_));
if (node_->IsBool()) return node_->GetBool() ? "true" : "false";
if (node_->IsInt64()) return std::to_string(node_->GetInt64());
if (node_->IsUint64()) return std::to_string(node_->GetUint64());
if (node_->IsString()) return std::string(node_->GetString(), node_->GetStringLength());
if (node_->IsDouble()) return std::to_string(node_->GetDouble());
throw std::runtime_error(fmt::format("Cannot convert non-scalar json value to string at '{}'", path_));
} catch (...) {
return nonstd::make_unexpected(std::current_exception());
}
}
std::string getDebugString() const override {
if (!node_) return "<invalid>";
if (node_->IsObject()) return "<Map>";
if (node_->IsArray()) return "<Array>";
if (node_->IsNull()) return "null";
if (auto int_str = getIntegerAsString()) {
return int_str.value();
}
if (node_->IsTrue()) return "true";
if (node_->IsFalse()) return "false";
if (node_->IsDouble()) return std::to_string(node_->GetDouble());
if (node_->IsString()) return '"' + std::string(node_->GetString(), node_->GetStringLength()) + '"';
return "<unknown>";
}
size_t size() const override {
if (!node_) {
throw std::runtime_error(fmt::format("Cannot get size of invalid json value at '{}'", path_));
}
if (!node_->IsArray()) {
throw std::runtime_error(fmt::format("Cannot get size of non-array json value at '{}'", path_));
}
return node_->Size();
}
flow::Node::Iterator begin() const override;
flow::Node::Iterator end() const override;
flow::Node operator[](std::string_view key) const override {
if (!node_ || node_->IsArray() || node_->IsNull()) {
return flow::Node{std::make_shared<JsonNode>(nullptr)};
}
if (!node_->IsObject()) {
throw std::runtime_error(fmt::format("Cannot get member '{}' of scalar json value at '{}'", key, path_));
}
auto it = node_->FindMember(rapidjson::Value(rapidjson::StringRef(key.data(), key.length())));
if (it == node_->MemberEnd()) {
return flow::Node{std::make_shared<JsonNode>(nullptr)};
}
return flow::Node{std::make_shared<JsonNode>(&it->value)};
}
std::optional<flow::Node::Cursor> getCursor() const override {
return std::nullopt;
}
private:
const rapidjson::Value* node_;
};
class JsonValueIterator : public flow::Node::Iterator::IteratorImpl {
public:
explicit JsonValueIterator(rapidjson::Value::ConstValueIterator it): it_(std::move(it)) {}
IteratorImpl& operator++() override {
++it_;
return *this;
}
bool operator==(const IteratorImpl& other) const override {
const auto* ptr = dynamic_cast<const JsonValueIterator*>(&other);
gsl_Expects(ptr);
return it_ == ptr->it_;
}
flow::Node::Iterator::Value operator*() const override {
auto node = flow::Node{std::make_shared<JsonNode>(&*it_)};
auto first = flow::Node{std::make_shared<JsonNode>(nullptr)};
auto second = flow::Node{std::make_shared<JsonNode>(nullptr)};
return {std::move(node), std::move(first), std::move(second)};
}
std::unique_ptr<IteratorImpl> clone() const override {
return std::make_unique<JsonValueIterator>(it_);
}
private:
rapidjson::Value::ConstValueIterator it_;
};
class JsonMemberIterator : public flow::Node::Iterator::IteratorImpl {
public:
explicit JsonMemberIterator(rapidjson::Value::ConstMemberIterator it): it_(std::move(it)) {}
IteratorImpl& operator++() override {
++it_;
return *this;
}
bool operator==(const IteratorImpl& other) const override {
const auto* ptr = dynamic_cast<const JsonMemberIterator*>(&other);
gsl_Expects(ptr);
return it_ == ptr->it_;
}
flow::Node::Iterator::Value operator*() const override {
auto node = flow::Node{std::make_shared<JsonNode>(nullptr)};
auto first = flow::Node{std::make_shared<JsonNode>(&it_->name)};
auto second = flow::Node{std::make_shared<JsonNode>(&it_->value)};
return flow::Node::Iterator::Value(node, first, second);
}
std::unique_ptr<IteratorImpl> clone() const override {
return std::make_unique<JsonMemberIterator>(it_);
}
private:
rapidjson::Value::ConstMemberIterator it_;
};
inline flow::Node::Iterator JsonNode::begin() const {
if (!node_) {
throw std::runtime_error(fmt::format("Cannot get begin of invalid json value at '{}'", path_));
}
if (node_->IsArray()) {
return flow::Node::Iterator{std::make_unique<JsonValueIterator>(node_->Begin())};
} else if (node_->IsObject()) {
return flow::Node::Iterator{std::make_unique<JsonMemberIterator>(node_->MemberBegin())};
} else {
throw std::runtime_error(fmt::format("Json node is not iterable, neither array nor object at '{}'", path_));
}
}
inline flow::Node::Iterator JsonNode::end() const {
if (!node_) {
throw std::runtime_error(fmt::format("Cannot get end of invalid json value at '{}'", path_));
}
if (node_->IsArray()) {
return flow::Node::Iterator{std::make_unique<JsonValueIterator>(node_->End())};
} else if (node_->IsObject()) {
return flow::Node::Iterator{std::make_unique<JsonMemberIterator>(node_->MemberEnd())};
} else {
throw std::runtime_error(fmt::format("Json node is not iterable, neither array nor object at '{}'", path_));
}
}
} // namespace org::apache::nifi::minifi::core