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.) :^)
This commit is contained in:
Andreas Kling 2019-08-07 21:28:07 +02:00
parent 43ec733b61
commit f6998b1817
Notes: sideshowbarker 2024-07-19 12:50:10 +09:00
11 changed files with 145 additions and 109 deletions

View file

@ -3,22 +3,4 @@
namespace AK { 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();
}
} }

View file

@ -44,8 +44,13 @@ public:
void append(const JsonValue& value) { m_values.append(value); } void append(const JsonValue& value) { m_values.append(value); }
void append(JsonValue&& value) { m_values.append(move(value)); } void append(JsonValue&& value) { m_values.append(move(value)); }
String serialized() const; template<typename Builder>
void serialize(StringBuilder&) const; typename Builder::OutputType serialized() const;
template<typename Builder>
void serialize(Builder&) const;
String to_string() const { return serialized<StringBuilder>(); }
template<typename Callback> template<typename Callback>
void for_each(Callback callback) const void for_each(Callback callback) const
@ -60,6 +65,26 @@ private:
Vector<JsonValue> m_values; Vector<JsonValue> m_values;
}; };
template<typename Builder>
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<typename Builder>
inline typename Builder::OutputType JsonArray::serialized() const
{
Builder builder;
serialize(builder);
return builder.build();
}
} }
using AK::JsonArray; using AK::JsonArray;

View file

@ -3,28 +3,4 @@
namespace AK { 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();
}
} }

View file

@ -2,6 +2,7 @@
#include <AK/AKString.h> #include <AK/AKString.h>
#include <AK/HashMap.h> #include <AK/HashMap.h>
#include <AK/JsonArray.h>
#include <AK/JsonValue.h> #include <AK/JsonValue.h>
namespace AK { namespace AK {
@ -63,13 +64,90 @@ public:
callback(it.key, it.value); callback(it.key, it.value);
} }
String serialized() const; template<typename Builder>
void serialize(StringBuilder&) const; typename Builder::OutputType serialized() const;
template<typename Builder>
void serialize(Builder&) const;
String to_string() const { return serialized<StringBuilder>(); }
private: private:
HashMap<String, JsonValue> m_members; HashMap<String, JsonValue> m_members;
}; };
template<typename Builder>
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<typename Builder>
inline typename Builder::OutputType JsonObject::serialized() const
{
Builder builder;
serialize(builder);
return builder.build();
}
template<typename Builder>
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>
typename Builder::OutputType JsonValue::serialized() const
{
Builder builder;
serialize(builder);
return builder.build();
}
} }
using AK::JsonObject; using AK::JsonObject;

View file

@ -158,50 +158,6 @@ void JsonValue::clear()
m_value.as_string = nullptr; 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) JsonValue JsonValue::from_string(const StringView& input)
{ {
return JsonParser(input).parse(); return JsonParser(input).parse();

View file

@ -3,6 +3,7 @@
#include <AK/AKString.h> #include <AK/AKString.h>
#include <AK/IPv4Address.h> #include <AK/IPv4Address.h>
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/StringBuilder.h>
namespace AK { namespace AK {
@ -57,14 +58,16 @@ public:
JsonValue& operator=(JsonArray&&) = delete; JsonValue& operator=(JsonArray&&) = delete;
JsonValue& operator=(JsonObject&&) = delete; JsonValue& operator=(JsonObject&&) = delete;
String serialized() const; template<typename Builder>
void serialize(StringBuilder&) const; typename Builder::OutputType serialized() const;
template<typename Builder>
void serialize(Builder&) const;
String to_string(const String& default_value = {}) const String to_string() const
{ {
if (is_string()) if (is_string())
return as_string(); return as_string();
return default_value; return serialized<StringBuilder>();
} }
Optional<IPv4Address> to_ipv4_address() const Optional<IPv4Address> to_ipv4_address() const
@ -151,7 +154,10 @@ public:
} }
#endif #endif
Type type() const { return m_type; } Type type() const
{
return m_type;
}
bool is_null() const { return m_type == Type::Null; } bool is_null() const { return m_type == Type::Null; }
bool is_undefined() const { return m_type == Type::Undefined; } 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_int() const { return m_type == Type::Int; }
bool is_uint() const { return m_type == Type::UnsignedInt; } bool is_uint() const { return m_type == Type::UnsignedInt; }
#ifndef KERNEL #ifndef KERNEL
bool is_double() const { return m_type == Type::Double; } bool is_double() const
{
return m_type == Type::Double;
}
#endif #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_object() const { return m_type == Type::Object; }
bool is_number() const bool is_number() const
{ {

View file

@ -8,6 +8,8 @@ namespace AK {
class StringBuilder { class StringBuilder {
public: public:
using OutputType = String;
explicit StringBuilder(int initial_capacity = 16); explicit StringBuilder(int initial_capacity = 16);
~StringBuilder() {} ~StringBuilder() {}
@ -17,6 +19,8 @@ public:
void appendf(const char*, ...); void appendf(const char*, ...);
void appendvf(const char*, va_list); void appendvf(const char*, va_list);
String build() { return to_string(); }
String to_string(); String to_string();
ByteBuffer to_byte_buffer(); ByteBuffer to_byte_buffer();

View file

@ -2,6 +2,7 @@
#include <AK/JsonObject.h> #include <AK/JsonObject.h>
#include <AK/JsonValue.h> #include <AK/JsonValue.h>
#include <AK/LogStream.h> #include <AK/LogStream.h>
#include <AK/StringBuilder.h>
#include <LibCore/CFile.h> #include <LibCore/CFile.h>
#include <stdio.h> #include <stdio.h>
@ -70,7 +71,7 @@ int main(int argc, char** argv)
auto class_name = widget_object.get("class").to_string(); auto class_name = widget_object.get("class").to_string();
dbg() << " " << name << " = new " << class_name << "(main_widget);"; 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") if (property_name == "class")
return; return;
@ -79,7 +80,7 @@ int main(int argc, char** argv)
if (property_value.is_null()) if (property_value.is_null())
value = "{}"; value = "{}";
else else
value = property_value.serialized(); value = property_value.to_string();
dbg() << " " << name << "->set_" << property_name << "(" << value << ");"; dbg() << " " << name << "->set_" << property_name << "(" << value << ");";
}); });

View file

@ -4,6 +4,7 @@
#include "VBWidgetRegistry.h" #include "VBWidgetRegistry.h"
#include <AK/JsonArray.h> #include <AK/JsonArray.h>
#include <AK/JsonObject.h> #include <AK/JsonObject.h>
#include <AK/StringBuilder.h>
#include <LibCore/CFile.h> #include <LibCore/CFile.h>
#include <LibGUI/GAction.h> #include <LibGUI/GAction.h>
#include <LibGUI/GMenu.h> #include <LibGUI/GMenu.h>
@ -318,7 +319,7 @@ void VBForm::load_from_file(const String& path)
(void)property_name; (void)property_name;
(void)property_value; (void)property_value;
VBProperty& property = vbwidget->property(property_name); 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); property.set_value(property_value);
}); });
m_widgets.append(vbwidget); m_widgets.append(vbwidget);
@ -349,7 +350,7 @@ void VBForm::write_to_file(const String& path)
widget_array.append(widget_object); widget_array.append(widget_object);
} }
form_object.set("widgets", widget_array); form_object.set("widgets", widget_array);
file.write(form_object.serialized()); file.write(form_object.to_string());
} }
void VBForm::dump() void VBForm::dump()

View file

@ -208,7 +208,7 @@ Optional<KBuffer> procfs$pid_fds(InodeIdentifier identifier)
description_object.set("offset", description->offset()); description_object.set("offset", description->offset());
array.append(move(description_object)); array.append(move(description_object));
} }
return array.serialized().to_byte_buffer(); return array.to_string().to_byte_buffer();
} }
Optional<KBuffer> procfs$pid_fd_entry(InodeIdentifier identifier) Optional<KBuffer> procfs$pid_fd_entry(InodeIdentifier identifier)
@ -241,7 +241,7 @@ Optional<KBuffer> procfs$pid_vm(InodeIdentifier identifier)
region_object.set("name", region.name()); region_object.set("name", region.name());
array.append(move(region_object)); array.append(move(region_object));
} }
return array.serialized().to_byte_buffer(); return array.to_string().to_byte_buffer();
} }
Optional<KBuffer> procfs$pci(InodeIdentifier) Optional<KBuffer> procfs$pci(InodeIdentifier)
@ -294,7 +294,7 @@ Optional<KBuffer> procfs$net_tcp(InodeIdentifier)
obj.set("sequence_number", socket->sequence_number()); obj.set("sequence_number", socket->sequence_number());
json.append(obj); json.append(obj);
}); });
return json.serialized().to_byte_buffer(); return json.to_string().to_byte_buffer();
} }
Optional<KBuffer> procfs$pid_vmo(InodeIdentifier identifier) Optional<KBuffer> procfs$pid_vmo(InodeIdentifier identifier)
@ -449,7 +449,7 @@ Optional<KBuffer> procfs$df(InodeIdentifier)
fs_object.set("mount_point", mount.absolute_path()); fs_object.set("mount_point", mount.absolute_path());
json.append(fs_object); json.append(fs_object);
}); });
return json.serialized().to_byte_buffer(); return json.to_string().to_byte_buffer();
} }
Optional<KBuffer> procfs$cpuinfo(InodeIdentifier) Optional<KBuffer> procfs$cpuinfo(InodeIdentifier)
@ -541,7 +541,7 @@ Optional<KBuffer> procfs$memstat(InodeIdentifier)
json.set("super_physical_available", MM.super_physical_pages()); json.set("super_physical_available", MM.super_physical_pages());
json.set("kmalloc_call_count", g_kmalloc_call_count); json.set("kmalloc_call_count", g_kmalloc_call_count);
json.set("kfree_call_count", g_kfree_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<KBuffer> procfs$all(InodeIdentifier) Optional<KBuffer> procfs$all(InodeIdentifier)
@ -577,7 +577,7 @@ Optional<KBuffer> procfs$all(InodeIdentifier)
build_process(*Scheduler::colonel()); build_process(*Scheduler::colonel());
for (auto* process : processes) for (auto* process : processes)
build_process(*process); build_process(*process);
return array.serialized().to_byte_buffer(); return array.to_string().to_byte_buffer();
} }
Optional<KBuffer> procfs$inodes(InodeIdentifier) Optional<KBuffer> procfs$inodes(InodeIdentifier)

View file

@ -1,6 +1,7 @@
#include <AK/JsonArray.h> #include <AK/JsonArray.h>
#include <AK/JsonObject.h> #include <AK/JsonObject.h>
#include <AK/JsonValue.h> #include <AK/JsonValue.h>
#include <AK/StringBuilder.h>
#include <LibCore/CFile.h> #include <LibCore/CFile.h>
#include <stdio.h> #include <stdio.h>
@ -65,6 +66,6 @@ void print(const JsonValue& value, int indent)
printf("\033[32;1m"); printf("\033[32;1m");
else if (value.is_null() || value.is_undefined()) else if (value.is_null() || value.is_undefined())
printf("\033[34;1m"); printf("\033[34;1m");
printf("%s", value.serialized().characters()); printf("%s", value.to_string().characters());
printf("\033[0m"); printf("\033[0m");
} }