/* * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2024, Dan Klishch * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #ifdef KERNEL # error "JsonValue does not propagate allocation failures, so it is not safe to use in the kernel." #endif #include #include #include #include #include namespace AK { class JsonValue { public: enum class Type { Null, Bool, Number, String, Array, Object, }; static ErrorOr from_string(StringView); JsonValue(); ~JsonValue(); JsonValue(JsonValue const&); JsonValue(JsonValue&&); JsonValue& operator=(JsonValue const&); JsonValue& operator=(JsonValue&&); JsonValue(int); JsonValue(unsigned); JsonValue(long); JsonValue(long unsigned); JsonValue(long long); JsonValue(long long unsigned); JsonValue(double); JsonValue(char const*); JsonValue(ByteString const&); JsonValue(StringView); template requires(SameAs, bool>) JsonValue(T value) : m_value { static_cast(value) } { } JsonValue(JsonArray const&); JsonValue(JsonObject const&); JsonValue(JsonArray&&); JsonValue(JsonObject&&); JsonValue& operator=(JsonArray const&); JsonValue& operator=(JsonObject const&); JsonValue& operator=(JsonArray&&); JsonValue& operator=(JsonObject&&); template typename Builder::OutputType serialized() const; template void serialize(Builder&) const; ByteString as_string_or(ByteString const& alternative) const { if (is_string()) return as_string(); return alternative; } ByteString deprecated_to_byte_string() const { if (is_string()) return as_string(); return serialized(); } Optional get_int() const { return get_integer(); } Optional get_i32() const { return get_integer(); } Optional get_i64() const { return get_integer(); } Optional get_uint() const { return get_integer(); } Optional get_u32() const { return get_integer(); } Optional get_u64() const { return get_integer(); } Optional get_float_with_precision_loss() const { return get_number_with_precision_loss(); } Optional get_double_with_precision_loss() const { return get_number_with_precision_loss(); } Optional get_addr() const { // Note: This makes the lambda dependent on the template parameter, which is necessary // for the `if constexpr` to not evaluate both branches. auto fn = [&]() -> Optional { if constexpr (IsSame) { return get_u64(); } else { return get_u32(); } }; return fn.operator()(); } Optional get_bool() const { if (!is_bool()) return {}; return as_bool(); } bool as_bool() const { return m_value.get(); } ByteString const& as_string() const { return m_value.get(); } JsonObject& as_object() { return *m_value.get>(); } JsonObject const& as_object() const { return *m_value.get>(); } JsonArray& as_array() { return *m_value.get>(); } JsonArray const& as_array() const { return *m_value.get>(); } Variant as_number() const { return m_value.downcast(); } Type type() const { return m_value.visit( [](Empty const&) { return Type::Null; }, [](bool const&) { return Type::Bool; }, [](Arithmetic auto const&) { return Type::Number; }, [](ByteString const&) { return Type::String; }, [](NonnullOwnPtr const&) { return Type::Array; }, [](NonnullOwnPtr const&) { return Type::Object; }); } bool is_null() const { return m_value.has(); } bool is_bool() const { return m_value.has(); } bool is_string() const { return m_value.has(); } bool is_array() const { return m_value.has>(); } bool is_object() const { return m_value.has>(); } bool is_number() const { return m_value.visit( [](bool const&) { return false; }, [](U const&) { return true; }, [](auto const&) { return false; }); } template Optional get_number_with_precision_loss() const { return m_value.visit( [](bool const&) { return Optional {}; }, [](U const& value) { return Optional { static_cast(value) }; }, [](auto const&) { return Optional {}; }); } template bool is_integer() const { return get_integer().has_value(); } template T as_integer() const { return get_integer().value(); } template Optional get_integer() const { return m_value.visit( [](bool const&) { return Optional {}; }, [](U const& value) -> Optional { if constexpr (Integral) { if (!is_within_range(value)) return {}; return static_cast(value); } else { // FIXME: Make is_within_range work with floating point numbers. if (static_cast(static_cast(value)) != value) return {}; return static_cast(value); } }, [](auto const&) { return Optional {}; }); } bool equals(JsonValue const& other) const; private: Variant< Empty, bool, i64, u64, double, ByteString, NonnullOwnPtr, NonnullOwnPtr> m_value; }; template<> struct Formatter : Formatter { ErrorOr format(FormatBuilder& builder, JsonValue const& value) { return Formatter::format(builder, value.serialized()); } }; } #if USING_AK_GLOBALLY using AK::JsonValue; #endif