summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormukai@chromium.org <mukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-22 18:31:47 +0000
committermukai@chromium.org <mukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-22 18:31:47 +0000
commit193f946be6f110983d57cb1c6eacb9fffd0bdd91 (patch)
treed155d69549ca9132077cf27a101df6a8b2d2635f
parent0bfbf88d75aae7adf722238e174dd11e07c9d133 (diff)
downloadchromium_src-193f946be6f110983d57cb1c6eacb9fffd0bdd91.zip
chromium_src-193f946be6f110983d57cb1c6eacb9fffd0bdd91.tar.gz
chromium_src-193f946be6f110983d57cb1c6eacb9fffd0bdd91.tar.bz2
Add JSONValueConverter.
JSONValueConverter will convert a Value object (from JSON) into a C++ struct. All you will do is to register the mapping between field name and actual C++ field, then: JSONValueConverter<Message> converter; Message msg; converter.Convert(json, &msg); will scan json and fill fields in msg. See comments in .h and unittests.cc for examples. BUG=chromium-os:23508 TEST=passed Review URL: http://codereview.chromium.org/8774002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@115562 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/base.gyp1
-rw-r--r--base/base.gypi2
-rw-r--r--base/json/json_value_converter.cc20
-rw-r--r--base/json/json_value_converter.h350
-rw-r--r--base/json/json_value_converter_unittest.cc116
5 files changed, 489 insertions, 0 deletions
diff --git a/base/base.gyp b/base/base.gyp
index 7295711..23eb7bb0 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -158,6 +158,7 @@
'i18n/string_search_unittest.cc',
'i18n/time_formatting_unittest.cc',
'json/json_reader_unittest.cc',
+ 'json/json_value_converter_unittest.cc',
'json/json_value_serializer_unittest.cc',
'json/json_writer_unittest.cc',
'json/string_escape_unittest.cc',
diff --git a/base/base.gypi b/base/base.gypi
index 472e70ae..7a99941 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -126,6 +126,8 @@
'id_map.h',
'json/json_reader.cc',
'json/json_reader.h',
+ 'json/json_value_converter.cc',
+ 'json/json_value_converter.h',
'json/json_value_serializer.cc',
'json/json_value_serializer.h',
'json/json_writer.cc',
diff --git a/base/json/json_value_converter.cc b/base/json/json_value_converter.cc
new file mode 100644
index 0000000..1870a0e
--- /dev/null
+++ b/base/json/json_value_converter.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/json/json_value_converter.h"
+
+#include <string>
+
+namespace base {
+namespace internal {
+
+FieldConverterBase::FieldConverterBase(const std::string& path)
+ : field_path_(path) {
+}
+
+FieldConverterBase::~FieldConverterBase() {
+}
+
+} // namespace internal
+} // namespace base
diff --git a/base/json/json_value_converter.h b/base/json/json_value_converter.h
new file mode 100644
index 0000000..64cd90b
--- /dev/null
+++ b/base/json/json_value_converter.h
@@ -0,0 +1,350 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_JSON_JSON_VALUE_CONVERTER_H_
+#define BASE_JSON_JSON_VALUE_CONVERTER_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "base/values.h"
+
+// JSONValueConverter converts a JSON value into a C++ struct in a
+// lightweight way.
+//
+// Usage:
+// For real examples, you may want to refer to _unittest.cc file.
+//
+// Assume that you have a struct like this:
+// struct Message {
+// int foo;
+// std::string bar;
+// static void RegisterJSONConverter(
+// JSONValueConverter<Message>* converter);
+// };
+//
+// And you want to parse a json data into this struct. First, you
+// need to declare RegisterJSONConverter() method in your struct.
+// // static
+// void Message::RegisterJSONConverter(
+// JSONValueConverter<Message>* converter) {
+// converter->RegisterIntField("foo", &Message::foo);
+// converter->RegisterStringField("bar", &Message::bar);
+// }
+//
+// Then, you just instantiate your JSONValueConverter of your type and call
+// Convert() method.
+// Message message;
+// JSONValueConverter<Message> converter;
+// converter.Convert(json, &message);
+//
+// For nested field, the internal message also has to implement the registration
+// method. Then, just use RegisterNestedField() from the containing struct's
+// RegisterJSONConverter method.
+// struct Nested {
+// Message foo;
+// void RegisterJSONConverter(...) {
+// ...
+// converter->RegisterNestedField("foo", &Nested::foo);
+// }
+// };
+//
+// For repeated field, we just assume std::vector for its container
+// and you can put RegisterRepeatedInt or some other types. Use
+// RegisterRepeatedMessage for nested repeated fields.
+//
+
+namespace base {
+
+template <typename StructType>
+class JSONValueConverter;
+
+namespace internal {
+
+class FieldConverterBase {
+ public:
+ BASE_EXPORT explicit FieldConverterBase(const std::string& path);
+ BASE_EXPORT virtual ~FieldConverterBase();
+ virtual void ConvertField(const base::Value& value, void* obj) const = 0;
+ const std::string& field_path() const { return field_path_; }
+
+ private:
+ std::string field_path_;
+ DISALLOW_COPY_AND_ASSIGN(FieldConverterBase);
+};
+
+template <typename FieldType>
+class ValueConverter {
+ public:
+ virtual ~ValueConverter() {}
+ virtual void Convert(const base::Value& value, FieldType* field) const = 0;
+};
+
+template <typename StructType, typename FieldType>
+class FieldConverter : public FieldConverterBase {
+ public:
+ explicit FieldConverter(const std::string& path,
+ FieldType StructType::* field,
+ ValueConverter<FieldType>* converter)
+ : FieldConverterBase(path),
+ field_pointer_(field),
+ value_converter_(converter) {
+ }
+
+ virtual void ConvertField(
+ const base::Value& value, void* obj) const OVERRIDE {
+ StructType* dst = reinterpret_cast<StructType*>(obj);
+ value_converter_->Convert(value, &(dst->*field_pointer_));
+ }
+
+ private:
+ FieldType StructType::* field_pointer_;
+ scoped_ptr<ValueConverter<FieldType> > value_converter_;
+ DISALLOW_COPY_AND_ASSIGN(FieldConverter);
+};
+
+template <typename FieldType>
+class BasicValueConverter;
+
+template <>
+class BasicValueConverter<int> : public ValueConverter<int> {
+ public:
+ BasicValueConverter() {}
+
+ virtual void Convert(const base::Value& value, int* field) const OVERRIDE {
+ value.GetAsInteger(field);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <>
+class BasicValueConverter<std::string> : public ValueConverter<std::string> {
+ public:
+ BasicValueConverter() {}
+
+ virtual void Convert(
+ const base::Value& value, std::string* field) const OVERRIDE {
+ value.GetAsString(field);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <>
+class BasicValueConverter<double> : public ValueConverter<double> {
+ public:
+ BasicValueConverter() {}
+
+ virtual void Convert(const base::Value& value, double* field) const OVERRIDE {
+ value.GetAsDouble(field);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <>
+class BasicValueConverter<bool> : public ValueConverter<bool> {
+ public:
+ BasicValueConverter() {}
+
+ virtual void Convert(const base::Value& value, bool* field) const OVERRIDE {
+ value.GetAsBoolean(field);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <typename NestedType>
+class NestedValueConverter : public ValueConverter<NestedType> {
+ public:
+ NestedValueConverter() {}
+
+ virtual void Convert(
+ const base::Value& value, NestedType* field) const OVERRIDE {
+ converter_.Convert(value, field);
+ }
+
+ private:
+ JSONValueConverter<NestedType> converter_;
+ DISALLOW_COPY_AND_ASSIGN(NestedValueConverter);
+};
+
+template <typename Element>
+class RepeatedValueConverter : public ValueConverter<std::vector<Element> > {
+ public:
+ RepeatedValueConverter() {}
+
+ virtual void Convert(
+ const base::Value& value, std::vector<Element>* field) const OVERRIDE {
+ const base::ListValue* list = NULL;
+ if (!value.GetAsList(&list)) {
+ // The field is not a list.
+ return;
+ }
+
+ field->reserve(list->GetSize());
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ base::Value* element = NULL;
+ if (!list->Get(i, &element))
+ continue;
+
+ Element e;
+ basic_converter_.Convert(*element, &e);
+ field->push_back(e);
+ }
+ }
+
+ private:
+ BasicValueConverter<Element> basic_converter_;
+ DISALLOW_COPY_AND_ASSIGN(RepeatedValueConverter);
+};
+
+template <typename NestedType>
+class RepeatedMessageConverter
+ : public ValueConverter<std::vector<NestedType> > {
+ public:
+ RepeatedMessageConverter() {}
+
+ virtual void Convert(
+ const base::Value& value, std::vector<NestedType>* field) const OVERRIDE {
+ const base::ListValue* list = NULL;
+ if (!value.GetAsList(&list))
+ return;
+
+ field->reserve(list->GetSize());
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ base::Value* element = NULL;
+ if (!list->Get(i, &element))
+ continue;
+
+ field->push_back(NestedType());
+ converter_.Convert(*element, &field->back());
+ }
+ }
+
+ private:
+ JSONValueConverter<NestedType> converter_;
+ DISALLOW_COPY_AND_ASSIGN(RepeatedMessageConverter);
+};
+
+} // namespace internal
+
+template <class StructType>
+class JSONValueConverter {
+ public:
+ JSONValueConverter() {
+ StructType::RegisterJSONConverter(this);
+ }
+
+ ~JSONValueConverter() {
+ STLDeleteContainerPointers(fields_.begin(), fields_.end());
+ }
+
+ void RegisterIntField(const std::string& field_name,
+ int StructType::* field) {
+ fields_.push_back(new internal::FieldConverter<StructType, int>(
+ field_name, field, new internal::BasicValueConverter<int>));
+ }
+
+ void RegisterStringField(const std::string& field_name,
+ std::string StructType::* field) {
+ fields_.push_back(new internal::FieldConverter<StructType, std::string>(
+ field_name, field, new internal::BasicValueConverter<std::string>));
+ }
+
+ void RegisterBoolField(const std::string& field_name,
+ bool StructType::* field) {
+ fields_.push_back(new internal::FieldConverter<StructType, bool>(
+ field_name, field, new internal::BasicValueConverter<bool>));
+ }
+
+ void RegisterDoubleField(const std::string& field_name,
+ double StructType::* field) {
+ fields_.push_back(new internal::FieldConverter<StructType, double>(
+ field_name, field, new internal::BasicValueConverter<double>));
+ }
+
+ template <class NestedType>
+ void RegisterNestedField(
+ const std::string& field_name, NestedType StructType::* field) {
+ fields_.push_back(new internal::FieldConverter<StructType, NestedType>(
+ field_name,
+ field,
+ new internal::NestedValueConverter<NestedType>));
+ }
+
+ void RegisterRepeatedInt(const std::string& field_name,
+ std::vector<int> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, std::vector<int> >(
+ field_name, field, new internal::RepeatedValueConverter<int>));
+ }
+
+ void RegisterRepeatedString(const std::string& field_name,
+ std::vector<std::string> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, std::vector<std::string> >(
+ field_name,
+ field,
+ new internal::RepeatedValueConverter<std::string>));
+ }
+
+ void RegisterRepeatedDouble(const std::string& field_name,
+ std::vector<double> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, std::vector<double> >(
+ field_name, field, new internal::RepeatedValueConverter<double>));
+ }
+
+ void RegisterRepeatedBool(const std::string& field_name,
+ std::vector<bool> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, std::vector<bool> >(
+ field_name, field, new internal::RepeatedValueConverter<bool>));
+ }
+
+ template <class NestedType>
+ void RegisterRepeatedMessage(const std::string& field_name,
+ std::vector<NestedType> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, std::vector<NestedType> >(
+ field_name,
+ field,
+ new internal::RepeatedMessageConverter<NestedType>));
+ }
+
+ void Convert(const base::Value& value, StructType* output) const {
+ const DictionaryValue* dictionary_value = NULL;
+ if (!value.GetAsDictionary(&dictionary_value))
+ return;
+
+ for(std::vector<internal::FieldConverterBase*>::const_iterator it =
+ fields_.begin(); it != fields_.end(); ++it) {
+ base::Value* field = NULL;
+ if (dictionary_value->Get((*it)->field_path(), &field)) {
+ (*it)->ConvertField(*field, output);
+ }
+ }
+ }
+
+ private:
+ std::vector<internal::FieldConverterBase*> fields_;
+
+ DISALLOW_COPY_AND_ASSIGN(JSONValueConverter);
+};
+
+} // namespace base
+
+#endif // BASE_JSON_JSON_VALUE_CONVERTER_H_
diff --git a/base/json/json_value_converter_unittest.cc b/base/json/json_value_converter_unittest.cc
new file mode 100644
index 0000000..d1a8ee72
--- /dev/null
+++ b/base/json/json_value_converter_unittest.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/json/json_value_converter.h"
+
+#include <string>
+#include <vector>
+
+#include "base/values.h"
+#include "base/json/json_reader.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+// Very simple messages.
+struct SimpleMessage {
+ int foo;
+ std::string bar;
+ bool baz;
+ std::vector<int> ints;
+ SimpleMessage() : foo(0), baz(false) {}
+
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<SimpleMessage>* converter) {
+ converter->RegisterIntField("foo", &SimpleMessage::foo);
+ converter->RegisterStringField("bar", &SimpleMessage::bar);
+ converter->RegisterBoolField("baz", &SimpleMessage::baz);
+ converter->RegisterRepeatedInt("ints", &SimpleMessage::ints);
+ }
+};
+
+// For nested messages.
+struct NestedMessage {
+ double foo;
+ SimpleMessage child;
+ std::vector<SimpleMessage> children;
+
+ NestedMessage() : foo(0) {}
+
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<NestedMessage>* converter) {
+ converter->RegisterDoubleField("foo", &NestedMessage::foo);
+ converter->RegisterNestedField("child", &NestedMessage::child);
+ converter->RegisterRepeatedMessage("children", &NestedMessage::children);
+ }
+};
+
+} // namespace
+
+TEST(JSONValueConverterTest, ParseSimpleMessage) {
+ const char normal_data[] =
+ "{\n"
+ " \"foo\": 1,\n"
+ " \"bar\": \"bar\",\n"
+ " \"baz\": true,\n"
+ " \"ints\": [1, 2]"
+ "}\n";
+
+ scoped_ptr<Value> value(base::JSONReader::Read(normal_data, false));
+ SimpleMessage message;
+ base::JSONValueConverter<SimpleMessage> converter;
+ converter.Convert(*value.get(), &message);
+ EXPECT_EQ(1, message.foo);
+ EXPECT_EQ("bar", message.bar);
+ EXPECT_TRUE(message.baz);
+ EXPECT_EQ(2, static_cast<int>(message.ints.size()));
+ EXPECT_EQ(1, message.ints[0]);
+ EXPECT_EQ(2, message.ints[1]);
+}
+
+TEST(JSONValueConverterTest, ParseNestedMessage) {
+ const char normal_data[] =
+ "{\n"
+ " \"foo\": 1.0,\n"
+ " \"child\": {\n"
+ " \"foo\": 1,\n"
+ " \"bar\": \"bar\",\n"
+ " \"baz\": true\n"
+ " },\n"
+ " \"children\": [{\n"
+ " \"foo\": 2,\n"
+ " \"bar\": \"foobar\",\n"
+ " \"baz\": true\n"
+ " },\n"
+ " {\n"
+ " \"foo\": 3,\n"
+ " \"bar\": \"barbaz\",\n"
+ " \"baz\": false\n"
+ " }]\n"
+ "}\n";
+
+ scoped_ptr<Value> value(base::JSONReader::Read(normal_data, false));
+ NestedMessage message;
+ base::JSONValueConverter<NestedMessage> converter;
+ converter.Convert(*value.get(), &message);
+ EXPECT_EQ(1.0, message.foo);
+ EXPECT_EQ(1, message.child.foo);
+ EXPECT_EQ("bar", message.child.bar);
+ EXPECT_TRUE(message.child.baz);
+
+ EXPECT_EQ(2, static_cast<int>(message.children.size()));
+ const SimpleMessage& first_child = message.children[0];
+ EXPECT_EQ(2, first_child.foo);
+ EXPECT_EQ("foobar", first_child.bar);
+ EXPECT_TRUE(first_child.baz);
+
+ const SimpleMessage& second_child = message.children[1];
+ EXPECT_EQ(3, second_child.foo);
+ EXPECT_EQ("barbaz", second_child.bar);
+ EXPECT_FALSE(second_child.baz);
+}
+
+} // namespace base