From f6998b1817a993e9a9da66244e5a26dfabc6f24e Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 7 Aug 2019 21:28:07 +0200 Subject: [PATCH] JSON: Templatize the JSON serialization code This makes it possible to use something other than a StringBuilder for serialization (and to produce something other than a String.) :^) --- AK/JsonArray.cpp | 18 ------- AK/JsonArray.h | 29 ++++++++++- AK/JsonObject.cpp | 24 --------- AK/JsonObject.h | 84 +++++++++++++++++++++++++++++-- AK/JsonValue.cpp | 44 ---------------- AK/JsonValue.h | 26 +++++++--- AK/StringBuilder.h | 4 ++ DevTools/FormCompiler/main.cpp | 5 +- DevTools/VisualBuilder/VBForm.cpp | 5 +- Kernel/FileSystem/ProcFS.cpp | 12 ++--- Userland/jp.cpp | 3 +- 11 files changed, 145 insertions(+), 109 deletions(-) diff --git a/AK/JsonArray.cpp b/AK/JsonArray.cpp index 2a877c1df5b..08cf6a96d4c 100644 --- a/AK/JsonArray.cpp +++ b/AK/JsonArray.cpp @@ -3,22 +3,4 @@ namespace AK { -void JsonArray::serialize(StringBuilder& builder) const -{ - builder.append('['); - for (int i = 0; i < m_values.size(); ++i) { - m_values[i].serialize(builder); - if (i != size() - 1) - builder.append(','); - } - builder.append(']'); -} - -String JsonArray::serialized() const -{ - StringBuilder builder; - serialize(builder); - return builder.to_string(); -} - } diff --git a/AK/JsonArray.h b/AK/JsonArray.h index afba071cb85..9d84d0908b0 100644 --- a/AK/JsonArray.h +++ b/AK/JsonArray.h @@ -44,8 +44,13 @@ public: void append(const JsonValue& value) { m_values.append(value); } void append(JsonValue&& value) { m_values.append(move(value)); } - String serialized() const; - void serialize(StringBuilder&) const; + template + typename Builder::OutputType serialized() const; + + template + void serialize(Builder&) const; + + String to_string() const { return serialized(); } template void for_each(Callback callback) const @@ -60,6 +65,26 @@ private: Vector m_values; }; +template +inline void JsonArray::serialize(Builder& builder) const +{ + builder.append('['); + for (int i = 0; i < m_values.size(); ++i) { + m_values[i].serialize(builder); + if (i != size() - 1) + builder.append(','); + } + builder.append(']'); +} + +template +inline typename Builder::OutputType JsonArray::serialized() const +{ + Builder builder; + serialize(builder); + return builder.build(); +} + } using AK::JsonArray; diff --git a/AK/JsonObject.cpp b/AK/JsonObject.cpp index f3b53be49e5..325ed33f1b9 100644 --- a/AK/JsonObject.cpp +++ b/AK/JsonObject.cpp @@ -3,28 +3,4 @@ namespace AK { -void JsonObject::serialize(StringBuilder& builder) const -{ - int index = 0; - builder.append('{'); - for_each_member([&] (auto& key, auto& value) { - builder.append('"'); - builder.append(key); - builder.append('"'); - builder.append(':'); - value.serialize(builder); - if (index != size() - 1) - builder.append(','); - ++index; - }); - builder.append('}'); -} - -String JsonObject::serialized() const -{ - StringBuilder builder; - serialize(builder); - return builder.to_string(); -} - } diff --git a/AK/JsonObject.h b/AK/JsonObject.h index a21e9f03c3c..a8b3e44523a 100644 --- a/AK/JsonObject.h +++ b/AK/JsonObject.h @@ -2,13 +2,14 @@ #include #include +#include #include namespace AK { class JsonObject { public: - JsonObject() { } + JsonObject() {} ~JsonObject() {} JsonObject(const JsonObject& other) @@ -63,13 +64,90 @@ public: callback(it.key, it.value); } - String serialized() const; - void serialize(StringBuilder&) const; + template + typename Builder::OutputType serialized() const; + + template + void serialize(Builder&) const; + + String to_string() const { return serialized(); } private: HashMap m_members; }; +template +inline void JsonObject::serialize(Builder& builder) const +{ + int index = 0; + builder.append('{'); + for_each_member([&](auto& key, auto& value) { + builder.append('"'); + builder.append(key); + builder.append('"'); + builder.append(':'); + value.serialize(builder); + if (index != size() - 1) + builder.append(','); + ++index; + }); + builder.append('}'); +} + +template +inline typename Builder::OutputType JsonObject::serialized() const +{ + Builder builder; + serialize(builder); + return builder.build(); +} + +template +void JsonValue::serialize(Builder& builder) const +{ + switch (m_type) { + case Type::String: + builder.appendf("\"%s\"", m_value.as_string->characters()); + break; + case Type::Array: + m_value.as_array->serialize(builder); + break; + case Type::Object: + m_value.as_object->serialize(builder); + break; + case Type::Bool: + builder.append(m_value.as_bool ? "true" : "false"); + break; +#ifndef KERNEL + case Type::Double: + builder.appendf("%g", m_value.as_double); + break; +#endif + case Type::Int: + builder.appendf("%d", m_value.as_int); + break; + case Type::UnsignedInt: + builder.appendf("%u", m_value.as_uint); + break; + case Type::Undefined: + builder.append("undefined"); + break; + case Type::Null: + builder.append("null"); + break; + default: + ASSERT_NOT_REACHED(); + } +} + +template +typename Builder::OutputType JsonValue::serialized() const +{ + Builder builder; + serialize(builder); + return builder.build(); +} + } using AK::JsonObject; diff --git a/AK/JsonValue.cpp b/AK/JsonValue.cpp index 27e06637ed8..0bbf9b74584 100644 --- a/AK/JsonValue.cpp +++ b/AK/JsonValue.cpp @@ -158,50 +158,6 @@ void JsonValue::clear() m_value.as_string = nullptr; } -void JsonValue::serialize(StringBuilder& builder) const -{ - switch (m_type) { - case Type::String: - builder.appendf("\"%s\"", m_value.as_string->characters()); - break; - case Type::Array: - m_value.as_array->serialize(builder); - break; - case Type::Object: - m_value.as_object->serialize(builder); - break; - case Type::Bool: - builder.append(m_value.as_bool ? "true" : "false"); - break; -#ifndef KERNEL - case Type::Double: - builder.appendf("%g", m_value.as_double); - break; -#endif - case Type::Int: - builder.appendf("%d", m_value.as_int); - break; - case Type::UnsignedInt: - builder.appendf("%u", m_value.as_uint); - break; - case Type::Undefined: - builder.append("undefined"); - break; - case Type::Null: - builder.append("null"); - break; - default: - ASSERT_NOT_REACHED(); - } -} - -String JsonValue::serialized() const -{ - StringBuilder builder; - serialize(builder); - return builder.to_string(); -} - JsonValue JsonValue::from_string(const StringView& input) { return JsonParser(input).parse(); diff --git a/AK/JsonValue.h b/AK/JsonValue.h index 19d95cce3ab..1c31a46dd92 100644 --- a/AK/JsonValue.h +++ b/AK/JsonValue.h @@ -3,6 +3,7 @@ #include #include #include +#include namespace AK { @@ -57,14 +58,16 @@ public: JsonValue& operator=(JsonArray&&) = delete; JsonValue& operator=(JsonObject&&) = delete; - String serialized() const; - void serialize(StringBuilder&) const; + template + typename Builder::OutputType serialized() const; + template + void serialize(Builder&) const; - String to_string(const String& default_value = {}) const + String to_string() const { if (is_string()) return as_string(); - return default_value; + return serialized(); } Optional to_ipv4_address() const @@ -151,7 +154,10 @@ public: } #endif - Type type() const { return m_type; } + Type type() const + { + return m_type; + } bool is_null() const { return m_type == Type::Null; } bool is_undefined() const { return m_type == Type::Undefined; } @@ -160,9 +166,15 @@ public: bool is_int() const { return m_type == Type::Int; } bool is_uint() const { return m_type == Type::UnsignedInt; } #ifndef KERNEL - bool is_double() const { return m_type == Type::Double; } + bool is_double() const + { + return m_type == Type::Double; + } #endif - bool is_array() const { return m_type == Type::Array; } + bool is_array() const + { + return m_type == Type::Array; + } bool is_object() const { return m_type == Type::Object; } bool is_number() const { diff --git a/AK/StringBuilder.h b/AK/StringBuilder.h index 744c71ef4b2..30b123ea0bb 100644 --- a/AK/StringBuilder.h +++ b/AK/StringBuilder.h @@ -8,6 +8,8 @@ namespace AK { class StringBuilder { public: + using OutputType = String; + explicit StringBuilder(int initial_capacity = 16); ~StringBuilder() {} @@ -17,6 +19,8 @@ public: void appendf(const char*, ...); void appendvf(const char*, va_list); + String build() { return to_string(); } + String to_string(); ByteBuffer to_byte_buffer(); diff --git a/DevTools/FormCompiler/main.cpp b/DevTools/FormCompiler/main.cpp index 2165b150fb3..2ffe14da1ff 100644 --- a/DevTools/FormCompiler/main.cpp +++ b/DevTools/FormCompiler/main.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -70,7 +71,7 @@ int main(int argc, char** argv) auto class_name = widget_object.get("class").to_string(); dbg() << " " << name << " = new " << class_name << "(main_widget);"; - widget_object.for_each_member([&](auto& property_name, auto& property_value) { + widget_object.for_each_member([&](auto& property_name, const JsonValue& property_value) { if (property_name == "class") return; @@ -79,7 +80,7 @@ int main(int argc, char** argv) if (property_value.is_null()) value = "{}"; else - value = property_value.serialized(); + value = property_value.to_string(); dbg() << " " << name << "->set_" << property_name << "(" << value << ");"; }); diff --git a/DevTools/VisualBuilder/VBForm.cpp b/DevTools/VisualBuilder/VBForm.cpp index 3cf2fa0dfdb..446177816fc 100644 --- a/DevTools/VisualBuilder/VBForm.cpp +++ b/DevTools/VisualBuilder/VBForm.cpp @@ -4,6 +4,7 @@ #include "VBWidgetRegistry.h" #include #include +#include #include #include #include @@ -318,7 +319,7 @@ void VBForm::load_from_file(const String& path) (void)property_name; (void)property_value; VBProperty& property = vbwidget->property(property_name); - dbgprintf("Set property %s.%s to '%s'\n", widget_class.characters(), property_name.characters(), property_value.serialized().characters()); + dbgprintf("Set property %s.%s to '%s'\n", widget_class.characters(), property_name.characters(), property_value.to_string().characters()); property.set_value(property_value); }); m_widgets.append(vbwidget); @@ -349,7 +350,7 @@ void VBForm::write_to_file(const String& path) widget_array.append(widget_object); } form_object.set("widgets", widget_array); - file.write(form_object.serialized()); + file.write(form_object.to_string()); } void VBForm::dump() diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index b6f0058b6dc..601c2fc6cb9 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -208,7 +208,7 @@ Optional procfs$pid_fds(InodeIdentifier identifier) description_object.set("offset", description->offset()); array.append(move(description_object)); } - return array.serialized().to_byte_buffer(); + return array.to_string().to_byte_buffer(); } Optional procfs$pid_fd_entry(InodeIdentifier identifier) @@ -241,7 +241,7 @@ Optional procfs$pid_vm(InodeIdentifier identifier) region_object.set("name", region.name()); array.append(move(region_object)); } - return array.serialized().to_byte_buffer(); + return array.to_string().to_byte_buffer(); } Optional procfs$pci(InodeIdentifier) @@ -294,7 +294,7 @@ Optional procfs$net_tcp(InodeIdentifier) obj.set("sequence_number", socket->sequence_number()); json.append(obj); }); - return json.serialized().to_byte_buffer(); + return json.to_string().to_byte_buffer(); } Optional procfs$pid_vmo(InodeIdentifier identifier) @@ -449,7 +449,7 @@ Optional procfs$df(InodeIdentifier) fs_object.set("mount_point", mount.absolute_path()); json.append(fs_object); }); - return json.serialized().to_byte_buffer(); + return json.to_string().to_byte_buffer(); } Optional procfs$cpuinfo(InodeIdentifier) @@ -541,7 +541,7 @@ Optional procfs$memstat(InodeIdentifier) json.set("super_physical_available", MM.super_physical_pages()); json.set("kmalloc_call_count", g_kmalloc_call_count); json.set("kfree_call_count", g_kfree_call_count); - return json.serialized().to_byte_buffer(); + return json.to_string().to_byte_buffer(); } Optional procfs$all(InodeIdentifier) @@ -577,7 +577,7 @@ Optional procfs$all(InodeIdentifier) build_process(*Scheduler::colonel()); for (auto* process : processes) build_process(*process); - return array.serialized().to_byte_buffer(); + return array.to_string().to_byte_buffer(); } Optional procfs$inodes(InodeIdentifier) diff --git a/Userland/jp.cpp b/Userland/jp.cpp index 9c4221e3da7..023a12085f9 100644 --- a/Userland/jp.cpp +++ b/Userland/jp.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -65,6 +66,6 @@ void print(const JsonValue& value, int indent) printf("\033[32;1m"); else if (value.is_null() || value.is_undefined()) printf("\033[34;1m"); - printf("%s", value.serialized().characters()); + printf("%s", value.to_string().characters()); printf("\033[0m"); }