Explorar el Código

AK: Add some classes for JSON encoding.

This patch adds JsonValue, JsonObject and JsonArray. You can use them to
build up a JsonObject and then serialize it to a string via to_string().

This patch only implements encoding, no decoding yet.
Andreas Kling hace 6 años
padre
commit
04a8fc9bd7
Se han modificado 7 ficheros con 301 adiciones y 0 borrados
  1. 15 0
      AK/JsonArray.cpp
  2. 24 0
      AK/JsonArray.h
  3. 21 0
      AK/JsonObject.cpp
  4. 45 0
      AK/JsonObject.h
  5. 140 0
      AK/JsonValue.cpp
  6. 53 0
      AK/JsonValue.h
  7. 3 0
      LibC/Makefile

+ 15 - 0
AK/JsonArray.cpp

@@ -0,0 +1,15 @@
+#include <AK/JsonArray.h>
+#include <AK/StringBuilder.h>
+
+String JsonArray::to_string() const
+{
+    StringBuilder builder;
+    builder.append('[');
+    for (int i = 0; i < m_values.size(); ++i) {
+        builder.append(m_values[i].to_string());
+        if (i != size() - 1)
+            builder.append(',');
+    }
+    builder.append(']');
+    return builder.to_string();
+}

+ 24 - 0
AK/JsonArray.h

@@ -0,0 +1,24 @@
+#pragma once
+
+#include <AK/JsonValue.h>
+#include <AK/Vector.h>
+
+class JsonArray {
+public:
+    JsonArray() {}
+    ~JsonArray() {}
+
+    int size() const { return m_values.size(); }
+    bool is_empty() const { return m_values.is_empty(); }
+
+    const JsonValue& at(int index) const { return m_values.at(index); }
+    const JsonValue& operator[](int index) const { return at(index); }
+
+    void clear() { m_values.clear(); }
+    void append(const JsonValue& value) { m_values.append(value); }
+
+    String to_string() const;
+
+private:
+    Vector<JsonValue> m_values;
+};

+ 21 - 0
AK/JsonObject.cpp

@@ -0,0 +1,21 @@
+#include <AK/JsonObject.h>
+#include <AK/StringBuilder.h>
+
+String JsonObject::to_string() const
+{
+    StringBuilder builder;
+    int index = 0;
+    builder.append('{');
+    for_each_member([&] (auto& key, auto& value) {
+        builder.append('"');
+        builder.append(key);
+        builder.append('"');
+        builder.append(':');
+        builder.append(value.to_string());
+        if (index != size() - 1)
+            builder.append(',');
+        ++index;
+    });
+    builder.append('}');
+    return builder.to_string();
+}

+ 45 - 0
AK/JsonObject.h

@@ -0,0 +1,45 @@
+#pragma once
+
+#include <AK/AKString.h>
+#include <AK/HashMap.h>
+#include <AK/JsonValue.h>
+
+class JsonObject {
+public:
+    JsonObject() { }
+    ~JsonObject() { }
+
+    JsonObject(const JsonObject& other)
+    {
+        for (auto& it : other.m_members)
+            m_members.set(it.key, it.value);
+    }
+
+    int size() const { return m_members.size(); }
+    bool is_empty() const { return m_members.is_empty(); }
+
+    JsonValue get(const String& key) const
+    {
+        auto it = m_members.find(key);
+        if (it == m_members.end())
+            return JsonValue(JsonValue::Type::Undefined);
+        return (*it).value;
+    }
+
+    void set(const String& key, const JsonValue& value)
+    {
+        m_members.set(key, value);
+    }
+
+    template<typename Callback>
+    void for_each_member(Callback callback) const
+    {
+        for (auto& it : m_members)
+            callback(it.key, it.value);
+    }
+
+    String to_string() const;
+
+private:
+    HashMap<String, JsonValue> m_members;
+};

+ 140 - 0
AK/JsonValue.cpp

@@ -0,0 +1,140 @@
+#include <AK/JsonArray.h>
+#include <AK/JsonObject.h>
+#include <AK/JsonValue.h>
+
+JsonValue::JsonValue(Type type)
+    : m_type(type)
+{
+}
+
+JsonValue::JsonValue(const JsonValue& other)
+{
+    copy_from(other);
+}
+
+JsonValue& JsonValue::operator=(const JsonValue& other)
+{
+    if (this != &other) {
+        clear();
+        copy_from(other);
+    }
+    return *this;
+}
+
+void JsonValue::copy_from(const JsonValue& other)
+{
+    m_type = other.m_type;
+    switch (m_type) {
+    case Type::String:
+        m_value.as_string = other.m_value.as_string;
+        AK::retain_if_not_null(m_value.as_string);
+        break;
+    case Type::Object:
+        m_value.as_object = new JsonObject(*other.m_value.as_object);
+        break;
+    case Type::Array:
+        m_value.as_array = new JsonArray(*other.m_value.as_array);
+        break;
+    default:
+        m_value.as_string = other.m_value.as_string;
+        break;
+    }
+}
+
+JsonValue::JsonValue(JsonValue&& other)
+{
+    m_type = exchange(other.m_type, Type::Undefined);
+    m_value.as_string = exchange(other.m_value.as_string, nullptr);
+}
+
+JsonValue& JsonValue::operator=(JsonValue&& other)
+{
+    if (this != &other) {
+        m_type = exchange(other.m_type, Type::Undefined);
+        m_value.as_string = exchange(other.m_value.as_string, nullptr);
+    }
+    return *this;
+}
+
+JsonValue::JsonValue(int value)
+    : m_type(Type::Int)
+{
+    m_value.as_int = value;
+}
+
+JsonValue::JsonValue(double value)
+    : m_type(Type::Double)
+{
+    m_value.as_double = value;
+}
+
+JsonValue::JsonValue(bool value)
+    : m_type(Type::Bool)
+{
+    m_value.as_bool = value;
+}
+
+JsonValue::JsonValue(const String& value)
+{
+    if (value.is_null()) {
+        m_type = Type::Null;
+    } else {
+        m_type = Type::String;
+        m_value.as_string = const_cast<StringImpl*>(value.impl());
+        AK::retain_if_not_null(m_value.as_string);
+    }
+}
+
+JsonValue::JsonValue(const JsonObject& value)
+    : m_type(Type::Object)
+{
+    m_value.as_object = new JsonObject(value);
+}
+
+JsonValue::JsonValue(const JsonArray& value)
+    : m_type(Type::Array)
+{
+    m_value.as_array = new JsonArray(value);
+}
+
+void JsonValue::clear()
+{
+    switch (m_type) {
+    case Type::String:
+        AK::release_if_not_null(m_value.as_string);
+        break;
+    case Type::Object:
+        delete m_value.as_object;
+        break;
+    case Type::Array:
+        delete m_value.as_array;
+        break;
+    default:
+        break;
+    }
+    m_type = Type::Undefined;
+    m_value.as_string = nullptr;
+}
+
+String JsonValue::to_string() const
+{
+    switch (m_type) {
+    case Type::String:
+        return String::format("\"%s\"", m_value.as_string->characters());
+    case Type::Array:
+        return m_value.as_array->to_string();
+    case Type::Object:
+        return m_value.as_object->to_string();
+    case Type::Bool:
+        return m_value.as_bool ? "true" : "false";
+    case Type::Double:
+        return String::format("%g", m_value.as_double);
+    case Type::Int:
+        return String::format("%d", m_value.as_int);
+    case Type::Undefined:
+        return "undefined";
+    case Type::Null:
+        return "null";
+    }
+    ASSERT_NOT_REACHED();
+}

+ 53 - 0
AK/JsonValue.h

@@ -0,0 +1,53 @@
+#pragma once
+
+#include <AK/AKString.h>
+
+class JsonArray;
+class JsonObject;
+
+class JsonValue {
+public:
+    enum class Type {
+        Undefined,
+        Null,
+        Int,
+        Double,
+        Bool,
+        String,
+        Array,
+        Object,
+    };
+
+    explicit JsonValue(Type = Type::Null);
+    ~JsonValue() { clear(); }
+
+    JsonValue(const JsonValue&);
+    JsonValue(JsonValue&&);
+
+    JsonValue& operator=(const JsonValue&);
+    JsonValue& operator=(JsonValue&&);
+
+    JsonValue(int);
+    JsonValue(double);
+    JsonValue(bool);
+    JsonValue(const String&);
+    JsonValue(const JsonArray&);
+    JsonValue(const JsonObject&);
+
+    String to_string() const;
+
+private:
+    void clear();
+    void copy_from(const JsonValue&);
+
+    Type m_type { Type::Undefined };
+
+    union {
+        StringImpl* as_string { nullptr };
+        JsonArray* as_array;
+        JsonObject* as_object;
+        double as_double;
+        int as_int;
+        bool as_bool;
+    } m_value;
+};

+ 3 - 0
LibC/Makefile

@@ -7,6 +7,9 @@ AK_OBJS = \
     ../AK/StringBuilder.o \
     ../AK/FileSystemPath.o \
     ../AK/StdLibExtras.o \
+    ../AK/JsonValue.o \
+    ../AK/JsonArray.o \
+    ../AK/JsonObject.o \
     ../AK/MappedFile.o
 
 LIBC_OBJS = \