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
11 changed files with 145 additions and 109 deletions

View file

@ -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();
}
}

View file

@ -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>
typename Builder::OutputType serialized() const;
template<typename Builder>
void serialize(Builder&) const;
String to_string() const { return serialized<StringBuilder>(); }
template<typename Callback>
void for_each(Callback callback) const
@ -60,6 +65,26 @@ private:
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;

View file

@ -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();
}
}

View file

@ -2,13 +2,14 @@
#include <AK/AKString.h>
#include <AK/HashMap.h>
#include <AK/JsonArray.h>
#include <AK/JsonValue.h>
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>
typename Builder::OutputType serialized() const;
template<typename Builder>
void serialize(Builder&) const;
String to_string() const { return serialized<StringBuilder>(); }
private:
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;

View file

@ -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();

View file

@ -3,6 +3,7 @@
#include <AK/AKString.h>
#include <AK/IPv4Address.h>
#include <AK/Optional.h>
#include <AK/StringBuilder.h>
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>
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())
return as_string();
return default_value;
return serialized<StringBuilder>();
}
Optional<IPv4Address> 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
{

View file

@ -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();

View file

@ -2,6 +2,7 @@
#include <AK/JsonObject.h>
#include <AK/JsonValue.h>
#include <AK/LogStream.h>
#include <AK/StringBuilder.h>
#include <LibCore/CFile.h>
#include <stdio.h>
@ -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 << ");";
});

View file

@ -4,6 +4,7 @@
#include "VBWidgetRegistry.h"
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/StringBuilder.h>
#include <LibCore/CFile.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GMenu.h>
@ -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()

View file

@ -208,7 +208,7 @@ Optional<KBuffer> 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<KBuffer> procfs$pid_fd_entry(InodeIdentifier identifier)
@ -241,7 +241,7 @@ Optional<KBuffer> 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<KBuffer> procfs$pci(InodeIdentifier)
@ -294,7 +294,7 @@ Optional<KBuffer> 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<KBuffer> procfs$pid_vmo(InodeIdentifier identifier)
@ -449,7 +449,7 @@ Optional<KBuffer> 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<KBuffer> procfs$cpuinfo(InodeIdentifier)
@ -541,7 +541,7 @@ Optional<KBuffer> 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<KBuffer> procfs$all(InodeIdentifier)
@ -577,7 +577,7 @@ Optional<KBuffer> 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<KBuffer> procfs$inodes(InodeIdentifier)

View file

@ -1,6 +1,7 @@
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/JsonValue.h>
#include <AK/StringBuilder.h>
#include <LibCore/CFile.h>
#include <stdio.h>
@ -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");
}