buffet: GCD command defintion. Compound object type support.

Added support for "object" type. Refactored parameter validation
to make sure we have object schema context when we validate
a value of parameter.

Parameter |schema| was used in two different contexts, as both
a base parameter definition and as a custom object definition.
Renamed the former to be 'base_schema' and latter as
'object_schema' to remove the confusion.

Extracted common data type manipulation functions into
schema_utils.cc/.h files.

BUG=chromium:374860
TEST=All unit tests pass.

Change-Id: I6c3549849a258bcc94b3d754acd14e072438d140
Reviewed-on: https://chromium-review.googlesource.com/204793
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp
index c16d741..8f7ba6e 100644
--- a/buffet/buffet.gyp
+++ b/buffet/buffet.gyp
@@ -32,6 +32,7 @@
         'commands/prop_types.cc',
         'commands/prop_values.cc',
         'commands/schema_constants.cc',
+        'commands/schema_utils.cc',
         'data_encoding.cc',
         'dbus_constants.cc',
         'dbus_utils.cc',
@@ -84,6 +85,7 @@
         'async_event_sequencer_unittest.cc',
         'buffet_testrunner.cc',
         'commands/object_schema_unittest.cc',
+        'commands/schema_utils_unittest.cc',
         'data_encoding_unittest.cc',
         'device_registration_info_unittest.cc',
         'error_unittest.cc',
diff --git a/buffet/commands/object_schema.cc b/buffet/commands/object_schema.cc
index 19610be..0312735 100644
--- a/buffet/commands/object_schema.cc
+++ b/buffet/commands/object_schema.cc
@@ -41,14 +41,15 @@
 }
 
 bool ObjectSchema::FromJson(const base::DictionaryValue* value,
-                            const ObjectSchema* schema, ErrorPtr* error) {
+                            const ObjectSchema* object_schema,
+                            ErrorPtr* error) {
   Properties properties;
   base::DictionaryValue::Iterator iter(*value);
   while (!iter.IsAtEnd()) {
     std::string name = iter.key();
-    const PropType* schemaProp = schema ? schema->GetProp(iter.key()) :
-                                          nullptr;
-    if (!PropFromJson(iter.key(), iter.value(), schemaProp, &properties,
+    const PropType* base_schema =
+        object_schema ? object_schema->GetProp(iter.key()) : nullptr;
+    if (!PropFromJson(iter.key(), iter.value(), base_schema, &properties,
                       error))
       return false;
     iter.Advance();
@@ -84,21 +85,21 @@
 
 bool ObjectSchema::PropFromJson(const std::string& prop_name,
                                 const base::Value& value,
-                                const PropType* schemaProp,
+                                const PropType* base_schema,
                                 Properties* properties,
                                 ErrorPtr* error) const {
   if (value.IsType(base::Value::TYPE_STRING)) {
     // A string value is a short-hand object specification and provides
     // the parameter type.
-    return PropFromJsonString(prop_name, value, schemaProp, properties,
+    return PropFromJsonString(prop_name, value, base_schema, properties,
                               error);
   } else if (value.IsType(base::Value::TYPE_LIST)) {
     // One of the enumerated types.
-    return PropFromJsonArray(prop_name, value, schemaProp, properties,
+    return PropFromJsonArray(prop_name, value, base_schema, properties,
                              error);
   } else if (value.IsType(base::Value::TYPE_DICTIONARY)) {
     // Full parameter definition.
-    return PropFromJsonObject(prop_name, value, schemaProp, properties,
+    return PropFromJsonObject(prop_name, value, base_schema, properties,
                               error);
   }
   Error::AddToPrintf(error, commands::errors::kDomain,
@@ -109,7 +110,7 @@
 
 bool ObjectSchema::PropFromJsonString(const std::string& prop_name,
                                       const base::Value& value,
-                                      const PropType* schemaProp,
+                                      const PropType* base_schema,
                                       Properties* properties,
                                       ErrorPtr* error) const {
   std::string type_name;
@@ -118,17 +119,17 @@
   if (!prop)
     return false;
   base::DictionaryValue empty;
-  if (!prop->FromJson(&empty, schemaProp, error))
+  if (!prop->FromJson(&empty, base_schema, error))
     return false;
   properties->insert(std::make_pair(prop_name, std::move(prop)));
   return true;
 }
 
 static std::string DetectArrayType(const base::ListValue* list,
-                                   const PropType* schemaProp) {
+                                   const PropType* base_schema) {
   std::string type_name;
-  if (schemaProp) {
-    type_name = schemaProp->GetTypeAsString();
+  if (base_schema) {
+    type_name = base_schema->GetTypeAsString();
   } else if (list->GetSize() > 0) {
     const base::Value* first_element = nullptr;
     if (list->Get(0, &first_element)) {
@@ -145,6 +146,9 @@
       case base::Value::TYPE_STRING:
         type_name = PropType::GetTypeStringFromType(ValueType::String);
         break;
+      case base::Value::TYPE_DICTIONARY:
+        type_name = PropType::GetTypeStringFromType(ValueType::Object);
+        break;
       default:
         // The rest are unsupported.
         break;
@@ -156,12 +160,12 @@
 
 bool ObjectSchema::PropFromJsonArray(const std::string& prop_name,
                                      const base::Value& value,
-                                     const PropType* schemaProp,
+                                     const PropType* base_schema,
                                      Properties* properties,
                                      ErrorPtr* error) const {
   const base::ListValue* list = nullptr;
   CHECK(value.GetAsList(&list)) << "Unable to get array value";
-  std::string type_name = DetectArrayType(list, schemaProp);
+  std::string type_name = DetectArrayType(list, base_schema);
   if (type_name.empty())
     return ErrorInvalidTypeInfo(prop_name, error);
   std::unique_ptr<PropType> prop = CreatePropType(type_name, prop_name, error);
@@ -170,14 +174,14 @@
   base::DictionaryValue array_object;
   array_object.SetWithoutPathExpansion(commands::attributes::kOneOf_Enum,
                                        list->DeepCopy());
-  if (!prop->FromJson(&array_object, schemaProp, error))
+  if (!prop->FromJson(&array_object, base_schema, error))
     return false;
   properties->insert(std::make_pair(prop_name, std::move(prop)));
   return true;
 }
 
 static std::string DetectObjectType(const base::DictionaryValue* dict,
-                                    const PropType* schemaProp) {
+                                    const PropType* base_schema) {
   bool has_min_max = dict->HasKey(commands::attributes::kNumeric_Min) ||
                      dict->HasKey(commands::attributes::kNumeric_Max);
 
@@ -191,7 +195,7 @@
   // "min:0, max:0" instead of just forcing it to be only "min:0.0, max:0.0".
   // If we have "minimum" or "maximum", and we have a Double schema object,
   // treat this object as a Double (even if both min and max are integers).
-  if (has_min_max && schemaProp && schemaProp->GetType() == ValueType::Double)
+  if (has_min_max && base_schema && base_schema->GetType() == ValueType::Double)
     return PropType::GetTypeStringFromType(ValueType::Double);
 
   // If we have at least one "minimum" or "maximum" that is Double,
@@ -213,18 +217,22 @@
       dict->HasKey(commands::attributes::kString_MaxLength))
     return PropType::GetTypeStringFromType(ValueType::String);
 
-  // If we have "values", it's an array.
+  // If we have "properties", it's an object.
+  if (dict->HasKey(commands::attributes::kObject_Properties))
+    return PropType::GetTypeStringFromType(ValueType::Object);
+
+  // If we have "enum", it's an array. Detect type from array elements.
   const base::ListValue* list = nullptr;
   if (dict->GetListWithoutPathExpansion(
       commands::attributes::kOneOf_Enum, &list))
-    return DetectArrayType(list, schemaProp);
+    return DetectArrayType(list, base_schema);
 
   return std::string();
 }
 
 bool ObjectSchema::PropFromJsonObject(const std::string& prop_name,
                                       const base::Value& value,
-                                      const PropType* schemaProp,
+                                      const PropType* base_schema,
                                       Properties* properties,
                                       ErrorPtr* error) const {
   const base::DictionaryValue* dict = nullptr;
@@ -234,15 +242,15 @@
     if (!dict->GetString(commands::attributes::kType, &type_name))
       return ErrorInvalidTypeInfo(prop_name, error);
   } else {
-    type_name = DetectObjectType(dict, schemaProp);
+    type_name = DetectObjectType(dict, base_schema);
   }
   if (type_name.empty()) {
-    if (!schemaProp)
+    if (!base_schema)
       return ErrorInvalidTypeInfo(prop_name, error);
-    type_name = schemaProp->GetTypeAsString();
+    type_name = base_schema->GetTypeAsString();
   }
   std::unique_ptr<PropType> prop = CreatePropType(type_name, prop_name, error);
-  if (!prop || !prop->FromJson(dict, schemaProp, error))
+  if (!prop || !prop->FromJson(dict, base_schema, error))
     return false;
   properties->insert(std::make_pair(prop_name, std::move(prop)));
   return true;
diff --git a/buffet/commands/object_schema.h b/buffet/commands/object_schema.h
index 0e48129..d6f5069 100644
--- a/buffet/commands/object_schema.h
+++ b/buffet/commands/object_schema.h
@@ -9,8 +9,6 @@
 #include <memory>
 #include <string>
 
-#include <base/basictypes.h>
-
 #include "buffet/error.h"
 
 namespace base {
@@ -34,7 +32,11 @@
   // name and the value is the parameter type definition object.
   using Properties = std::map<std::string, std::shared_ptr<PropType>>;
 
+  // Declaring default and copy constructors to document the copyable
+  // nature of this class. Using the default implementation for them though.
   ObjectSchema() = default;
+  ObjectSchema(const ObjectSchema& rhs) = default;
+  ObjectSchema& operator=(const ObjectSchema& rhs) = default;
 
   // Add a new parameter definition.
   void AddProp(const std::string& name, std::shared_ptr<PropType> prop);
@@ -43,44 +45,54 @@
   // Gets the list of all the properties defined.
   const Properties& GetProps() const { return properties_; }
 
+  // Specify whether extra properties are allowed on objects described by
+  // this schema. When validating a value of an object type, we can
+  // make sure that the value has only the properties explicitly defined by
+  // the schema and no other (custom) properties are allowed.
+  // This is to support JSON Schema's "additionalProperties" specification.
+  bool GetExtraPropertiesAllowed() const { return extra_properties_allowed_; }
+  void SetExtraPropertiesAllowed(bool allowed) {
+    extra_properties_allowed_ = allowed;
+  }
+
   // Saves the object schema to JSON. When |full_schema| is set to true,
   // then all properties and constraints are saved, otherwise, only
   // the overridden (not inherited) ones are saved.
   std::unique_ptr<base::DictionaryValue> ToJson(bool full_schema,
                                                 ErrorPtr* error) const;
-  // Loads the object schema from JSON. If |schema| is not nullptr, it is
+  // Loads the object schema from JSON. If |object_schema| is not nullptr, it is
   // used as a base schema to inherit omitted properties and constraints from.
-  bool FromJson(const base::DictionaryValue* value, const ObjectSchema* schema,
-                ErrorPtr* error);
+  bool FromJson(const base::DictionaryValue* value,
+                const ObjectSchema* object_schema, ErrorPtr* error);
 
  private:
   // Internal helper method to load individual parameter type definitions.
   bool PropFromJson(const std::string& prop_name,
                     const base::Value& value,
-                    const PropType* schemaProp,
+                    const PropType* base_schema,
                     Properties* properties, ErrorPtr* error) const;
   // Helper function in case the parameter is defined as JSON string like this:
   //   "prop":"..."
   bool PropFromJsonString(const std::string& prop_name,
                           const base::Value& value,
-                          const PropType* schemaProp,
+                          const PropType* base_schema,
                           Properties* properties, ErrorPtr* error) const;
   // Helper function in case the parameter is defined as JSON array like this:
   //   "prop":[...]
   bool PropFromJsonArray(const std::string& prop_name,
                          const base::Value& value,
-                         const PropType* schemaProp,
+                         const PropType* base_schema,
                          Properties* properties, ErrorPtr* error) const;
   // Helper function in case the parameter is defined as JSON object like this:
   //   "prop":{...}
   bool PropFromJsonObject(const std::string& prop_name,
                           const base::Value& value,
-                          const PropType* schemaProp,
+                          const PropType* base_schema,
                           Properties* properties, ErrorPtr* error) const;
 
   // Internal parameter type definition map.
   Properties properties_;
-  DISALLOW_COPY_AND_ASSIGN(ObjectSchema);
+  bool extra_properties_allowed_{false};
 };
 
 }  // namespace buffet
diff --git a/buffet/commands/object_schema_unittest.cc b/buffet/commands/object_schema_unittest.cc
index fa901ae..be93073 100644
--- a/buffet/commands/object_schema_unittest.cc
+++ b/buffet/commands/object_schema_unittest.cc
@@ -60,6 +60,7 @@
   EXPECT_EQ(&prop, prop.GetInt());
   EXPECT_EQ(nullptr, prop.GetDouble());
   EXPECT_EQ(nullptr, prop.GetString());
+  EXPECT_EQ(nullptr, prop.GetObject());
 }
 
 TEST(CommandSchema, IntPropType_ToJson) {
@@ -165,6 +166,7 @@
   EXPECT_EQ(&prop, prop.GetBoolean());
   EXPECT_EQ(nullptr, prop.GetDouble());
   EXPECT_EQ(nullptr, prop.GetString());
+  EXPECT_EQ(nullptr, prop.GetObject());
 }
 
 TEST(CommandSchema, BoolPropType_ToJson) {
@@ -229,6 +231,7 @@
   EXPECT_EQ(nullptr, prop.GetBoolean());
   EXPECT_EQ(&prop, prop.GetDouble());
   EXPECT_EQ(nullptr, prop.GetString());
+  EXPECT_EQ(nullptr, prop.GetObject());
 }
 
 TEST(CommandSchema, DoublePropType_ToJson) {
@@ -325,6 +328,7 @@
   EXPECT_EQ(nullptr, prop.GetBoolean());
   EXPECT_EQ(nullptr, prop.GetDouble());
   EXPECT_EQ(&prop, prop.GetString());
+  EXPECT_EQ(nullptr, prop.GetObject());
 }
 
 TEST(CommandSchema, StringPropType_ToJson) {
@@ -411,6 +415,121 @@
   error.reset();
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+TEST(CommandSchema, ObjectPropType_Empty) {
+  buffet::ObjectPropType prop;
+  EXPECT_TRUE(prop.HasOverriddenAttributes());
+  EXPECT_FALSE(prop.IsBasedOnSchema());
+}
+
+TEST(CommandSchema, ObjectPropType_Types) {
+  buffet::ObjectPropType prop;
+  EXPECT_EQ(nullptr, prop.GetInt());
+  EXPECT_EQ(nullptr, prop.GetBoolean());
+  EXPECT_EQ(nullptr, prop.GetDouble());
+  EXPECT_EQ(nullptr, prop.GetString());
+  EXPECT_EQ(&prop, prop.GetObject());
+}
+
+TEST(CommandSchema, ObjectPropType_ToJson) {
+  buffet::ObjectPropType prop;
+  EXPECT_EQ("{'properties':{}}",
+            ValueToString(prop.ToJson(false, nullptr).get()));
+  EXPECT_EQ("{'properties':{},'type':'object'}",
+            ValueToString(prop.ToJson(true, nullptr).get()));
+  EXPECT_FALSE(prop.IsBasedOnSchema());
+  buffet::ObjectPropType prop2;
+  prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
+  EXPECT_EQ("{}", ValueToString(prop2.ToJson(false, nullptr).get()));
+  EXPECT_TRUE(prop2.IsBasedOnSchema());
+
+  auto schema = std::make_shared<buffet::ObjectSchema>();
+  schema->AddProp("expires", std::make_shared<buffet::IntPropType>());
+  auto pw = std::make_shared<buffet::StringPropType>();
+  pw->AddLengthConstraint(6, 100);
+  schema->AddProp("password", pw);
+  prop2.SetObjectSchema(schema);
+  EXPECT_EQ("{'properties':{'expires':'integer',"
+            "'password':{'maxLength':100,'minLength':6}}}",
+            ValueToString(prop2.ToJson(false, nullptr).get()));
+  EXPECT_EQ("{'properties':{'expires':{'type':'integer'},"
+            "'password':{'maxLength':100,'minLength':6,'type':'string'}},"
+            "'type':'object'}",
+            ValueToString(prop2.ToJson(true, nullptr).get()));
+}
+
+TEST(CommandSchema, ObjectPropType_FromJson) {
+  buffet::ObjectPropType base_prop;
+  EXPECT_TRUE(base_prop.FromJson(CreateDictionaryValue(
+      "{'properties':{'name':'string','age':'integer'}}").get(), nullptr,
+      nullptr));
+  auto schema = base_prop.GetObjectSchemaPtr();
+  const buffet::PropType* prop = schema->GetProp("name");
+  EXPECT_EQ(buffet::ValueType::String, prop->GetType());
+  prop = schema->GetProp("age");
+  EXPECT_EQ(buffet::ValueType::Int, prop->GetType());
+}
+
+TEST(CommandSchema, ObjectPropType_Validate) {
+  buffet::ObjectPropType prop;
+  prop.FromJson(CreateDictionaryValue(
+      "{'properties':{'expires':'integer',"
+      "'password':{'maxLength':100,'minLength':6}}}").get(), nullptr,
+      nullptr);
+  buffet::ErrorPtr error;
+  EXPECT_TRUE(prop.ValidateValue(CreateValue(
+      "{'expires':10,'password':'abcdef'}").get(), &error));
+  error.reset();
+
+  EXPECT_FALSE(prop.ValidateValue(CreateValue(
+      "{'expires':10}").get(), &error));
+  EXPECT_EQ("parameter_missing", error->GetCode());
+  error.reset();
+
+  EXPECT_FALSE(prop.ValidateValue(CreateValue(
+      "{'password':'abcdef'}").get(), &error));
+  EXPECT_EQ("parameter_missing", error->GetCode());
+  error.reset();
+
+  EXPECT_FALSE(prop.ValidateValue(CreateValue(
+      "{'expires':10,'password':'abcde'}").get(), &error));
+  EXPECT_EQ("out_of_range", error->GetFirstError()->GetCode());
+  error.reset();
+
+  EXPECT_FALSE(prop.ValidateValue(CreateValue("2").get(), &error));
+  EXPECT_EQ("type_mismatch", error->GetCode());
+  error.reset();
+
+  EXPECT_FALSE(prop.ValidateValue(CreateValue(
+      "{'expires':10,'password':'abcdef','retry':true}").get(), &error));
+  EXPECT_EQ("unexpected_parameter", error->GetCode());
+  error.reset();
+}
+
+TEST(CommandSchema, ObjectPropType_Validate_Enum) {
+  buffet::ObjectPropType prop;
+  EXPECT_TRUE(prop.FromJson(CreateDictionaryValue(
+      "{'properties':{'width':'integer','height':'integer'},"
+      "'enum':[{'width':10,'height':20},{'width':100,'height':200}]}").get(),
+      nullptr, nullptr));
+  buffet::ErrorPtr error;
+  EXPECT_TRUE(prop.ValidateValue(CreateValue(
+      "{'height':20,'width':10}").get(), &error));
+  error.reset();
+
+  EXPECT_TRUE(prop.ValidateValue(CreateValue(
+      "{'height':200,'width':100}").get(), &error));
+  error.reset();
+
+  EXPECT_FALSE(prop.ValidateValue(CreateValue(
+      "{'height':12,'width':10}").get(), &error));
+  EXPECT_EQ("out_of_range", error->GetCode());
+  error.reset();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeName) {
   buffet::ObjectSchema schema;
   const char* schema_str = "{"
diff --git a/buffet/commands/prop_constraints.cc b/buffet/commands/prop_constraints.cc
index 2376588..5ea9314 100644
--- a/buffet/commands/prop_constraints.cc
+++ b/buffet/commands/prop_constraints.cc
@@ -3,25 +3,12 @@
 // found in the LICENSE file.
 
 #include "buffet/commands/prop_constraints.h"
+#include "buffet/commands/prop_values.h"
 #include "buffet/commands/schema_constants.h"
 #include "buffet/string_utils.h"
 
 namespace buffet {
 
-// Specializations of TypedValueToJson<T>() for supported C++ types.
-std::unique_ptr<base::Value> TypedValueToJson(bool value) {
-  return std::unique_ptr<base::Value>(base::Value::CreateBooleanValue(value));
-}
-std::unique_ptr<base::Value> TypedValueToJson(int value) {
-  return std::unique_ptr<base::Value>(base::Value::CreateIntegerValue(value));
-}
-std::unique_ptr<base::Value> TypedValueToJson(double value) {
-  return std::unique_ptr<base::Value>(base::Value::CreateDoubleValue(value));
-}
-std::unique_ptr<base::Value> TypedValueToJson(const std::string& value) {
-  return std::unique_ptr<base::Value>(base::Value::CreateStringValue(value));
-}
-
 // Constraint ----------------------------------------------------------------
 Constraint::~Constraint() {}
 
@@ -76,7 +63,7 @@
 
 std::unique_ptr<base::Value> ConstraintStringLength::ToJson(
     ErrorPtr* error) const {
-  return TypedValueToJson(limit_.value);
+  return TypedValueToJson(limit_.value, error);
 }
 
 // ConstraintStringLengthMin --------------------------------------------------
@@ -85,9 +72,10 @@
 ConstraintStringLengthMin::ConstraintStringLengthMin(int limit)
     : ConstraintStringLength(limit) {}
 
-bool ConstraintStringLengthMin::Validate(const Any& value,
+bool ConstraintStringLengthMin::Validate(const PropValue& value,
                                          ErrorPtr* error) const {
-  std::string str = value.Get<std::string>();
+  CHECK(value.GetString()) << "Expecting a string value for this constraint";
+  const std::string& str = value.GetString()->GetValue();
   int length = static_cast<int>(str.size());
   if (length < limit_.value) {
     if (limit_.value == 1) {
@@ -116,9 +104,10 @@
 ConstraintStringLengthMax::ConstraintStringLengthMax(int limit)
     : ConstraintStringLength(limit) {}
 
-bool ConstraintStringLengthMax::Validate(const Any& value,
+bool ConstraintStringLengthMax::Validate(const PropValue& value,
                                          ErrorPtr* error) const {
-  std::string str = value.Get<std::string>();
+  CHECK(value.GetString()) << "Expecting a string value for this constraint";
+  const std::string& str = value.GetString()->GetValue();
   int length = static_cast<int>(str.size());
   if (length > limit_.value) {
     Error::AddToPrintf(error, commands::errors::kDomain,
diff --git a/buffet/commands/prop_constraints.h b/buffet/commands/prop_constraints.h
index 96bfdfb..da43e86 100644
--- a/buffet/commands/prop_constraints.h
+++ b/buffet/commands/prop_constraints.h
@@ -5,7 +5,6 @@
 #ifndef BUFFET_COMMANDS_PROP_CONSTRAINTS_H_
 #define BUFFET_COMMANDS_PROP_CONSTRAINTS_H_
 
-#include <limits>
 #include <string>
 #include <type_traits>
 #include <vector>
@@ -13,64 +12,14 @@
 #include <base/basictypes.h>
 #include <base/values.h>
 
-#include "buffet/any.h"
+#include "buffet/commands/prop_values.h"
 #include "buffet/commands/schema_constants.h"
+#include "buffet/commands/schema_utils.h"
 #include "buffet/error.h"
 #include "buffet/string_utils.h"
 
 namespace buffet {
 
-// InheritableAttribute class is used for specifying various command parameter
-// attributes that can be inherited from a base (parent) schema.
-// The |value| still specifies the actual attribute values, whether it
-// is inherited or overridden, while |is_inherited| can be used to identify
-// if the attribute was inherited (true) or overridden (false).
-template<typename T>
-class InheritableAttribute {
- public:
-  InheritableAttribute() = default;
-  explicit InheritableAttribute(T val)
-      : value(std::move(val)), is_inherited(true) {}
-  InheritableAttribute(T val, bool inherited)
-      : value(std::move(val)), is_inherited(inherited) {}
-  T value{};
-  bool is_inherited{true};
-};
-
-// A bunch of helper function to create base::Value for specific C++ classes,
-// including vectors of types. These are used in template classes below
-// to simplify specialization logic.
-std::unique_ptr<base::Value> TypedValueToJson(bool value);
-std::unique_ptr<base::Value> TypedValueToJson(int value);
-std::unique_ptr<base::Value> TypedValueToJson(double value);
-std::unique_ptr<base::Value> TypedValueToJson(const std::string& value);
-template<typename T>
-std::unique_ptr<base::Value> TypedValueToJson(const std::vector<T>& values) {
-  std::unique_ptr<base::ListValue> list(new base::ListValue);
-  for (const auto& v : values)
-    list->Append(TypedValueToJson(v).release());
-  return std::unique_ptr<base::Value>(list.release());
-}
-
-// Similarly to CreateTypedValue() function above, the following overloaded
-// helper methods allow to extract specific C++ data types from base::Value.
-// Also used in template classes below to simplify specialization logic.
-inline bool TypedValueFromJson(const base::Value* value_in, bool* value_out) {
-  return value_in->GetAsBoolean(value_out);
-}
-inline bool TypedValueFromJson(const base::Value* value_in, int* value_out) {
-  return value_in->GetAsInteger(value_out);
-}
-inline bool TypedValueFromJson(const base::Value* value_in, double* value_out) {
-  return value_in->GetAsDouble(value_out);
-}
-inline bool TypedValueFromJson(const base::Value* value_in,
-                               std::string* value_out) {
-  return value_in->GetAsString(value_out);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
 enum class ConstraintType {
   Min,
   Max,
@@ -97,7 +46,7 @@
   // Validates a parameter against the constraint. Returns true if parameter
   // value satisfies the constraint, otherwise fills the optional |error| with
   // the details for the failure.
-  virtual bool Validate(const Any& value, ErrorPtr* error) const = 0;
+  virtual bool Validate(const PropValue& value, ErrorPtr* error) const = 0;
   // Makes a copy of the constraint object, marking all the attributes
   // as inherited from the original definition.
   virtual std::shared_ptr<Constraint> CloneAsInherited() const = 0;
@@ -153,7 +102,7 @@
 
   // Implementation of Constraint::ToJson().
   virtual std::unique_ptr<base::Value> ToJson(ErrorPtr* error) const override {
-    return TypedValueToJson(limit_.value);
+    return TypedValueToJson(limit_.value, error);
   }
 
   // Stores the upper/lower value limit for maximum/minimum constraint.
@@ -178,8 +127,9 @@
   virtual ConstraintType GetType() const { return ConstraintType::Min; }
 
   // Implementation of Constraint::Validate().
-  virtual bool Validate(const Any& value, ErrorPtr* error) const override {
-    const T& v = value.Get<T>();
+  virtual bool Validate(const PropValue& value,
+                        ErrorPtr* error) const override {
+    T v = value.GetValueAsAny().Get<T>();
     if (v < this->limit_.value)
       return this->ReportErrorLessThan(
           error, string_utils::ToString(v),
@@ -214,8 +164,9 @@
   virtual ConstraintType GetType() const { return ConstraintType::Max; }
 
   // Implementation of Constraint::Validate().
-  virtual bool Validate(const Any& value, ErrorPtr* error) const override {
-    const T& v = value.Get<T>();
+  virtual bool Validate(const PropValue& value,
+                        ErrorPtr* error) const override {
+    T v = value.GetValueAsAny().Get<T>();
     if (v > this->limit_.value)
       return this->ReportErrorGreaterThan(
           error, string_utils::ToString(v),
@@ -268,7 +219,7 @@
     return ConstraintType::StringLengthMin;
   }
   // Implementation of Constraint::Validate().
-  virtual bool Validate(const Any& value, ErrorPtr* error) const override;
+  virtual bool Validate(const PropValue& value, ErrorPtr* error) const override;
   // Implementation of Constraint::CloneAsInherited().
   virtual std::shared_ptr<Constraint> CloneAsInherited() const override;
   // Implementation of Constraint::GetDictKey().
@@ -289,7 +240,7 @@
     return ConstraintType::StringLengthMax;
   }
   // Implementation of Constraint::Validate().
-  virtual bool Validate(const Any& value, ErrorPtr* error) const override;
+  virtual bool Validate(const PropValue& value, ErrorPtr* error) const override;
   // Implementation of Constraint::CloneAsInherited().
   virtual std::shared_ptr<Constraint> CloneAsInherited() const override;
   // Implementation of Constraint::GetDictKey().
@@ -301,27 +252,6 @@
   DISALLOW_COPY_AND_ASSIGN(ConstraintStringLengthMax);
 };
 
-// CompareValue is a helper functor to help with implementing EqualsTo operator
-// for various data types. For most scalar types it is using operator==(),
-// however, for floating point values, rounding errors in binary representation
-// of IEEE floats/doubles can cause straight == comparison to fail for seemingly
-// equivalent values. For these, use approximate comparison with the error
-// margin equal to the epsilon value defined for the corresponding data type.
-
-// Compare exact types using ==.
-template<typename T, bool exact = true>
-struct CompareValue {
-  inline bool operator()(const T& v1, const T& v2) { return v1 == v2; }
-};
-
-// Compare non-exact types (such as double) using precision margin (epsilon).
-template<typename T>
-struct CompareValue<T, false> {
-  inline bool operator()(const T& v1, const T& v2) {
-    return std::abs(v1 - v2) <= std::numeric_limits<T>::epsilon();
-  }
-};
-
 // Implementation of OneOf constraint for different data types.
 template<typename T>
 class ConstraintOneOf : public Constraint {
@@ -342,20 +272,20 @@
   }
 
   // Implementation of Constraint::Validate().
-  virtual bool Validate(const Any& value, ErrorPtr* error) const override {
-    const T& v = value.Get<T>();
-    constexpr bool exact_type = std::is_same<T, std::string>::value ||
-                                std::numeric_limits<T>::is_exact;
+  virtual bool Validate(const PropValue& value,
+                        ErrorPtr* error) const override {
+    using string_utils::ToString;
+    T v = value.GetValueAsAny().Get<T>();
     for (const auto& item : set_.value) {
-      if (CompareValue<T, exact_type>()(v, item))
+      if (CompareValue(v, item))
         return true;
     }
     std::vector<std::string> values;
     values.reserve(set_.value.size());
     for (const auto& item : set_.value) {
-      values.push_back(string_utils::ToString(item));
+      values.push_back(ToString(item));
     }
-    return ReportErrorNotOneOf(error, string_utils::ToString(v), values);
+    return ReportErrorNotOneOf(error, ToString(v), values);
   }
 
   // Implementation of Constraint::CloneAsInherited().
@@ -365,7 +295,7 @@
 
   // Implementation of Constraint::ToJson().
   virtual std::unique_ptr<base::Value> ToJson(ErrorPtr* error) const override {
-    return TypedValueToJson(set_.value);
+    return TypedValueToJson(set_.value, error);
   }
 
   // Implementation of Constraint::GetDictKey().
diff --git a/buffet/commands/prop_types.cc b/buffet/commands/prop_types.cc
index 6efdfb6..3b71a56 100644
--- a/buffet/commands/prop_types.cc
+++ b/buffet/commands/prop_types.cc
@@ -39,11 +39,11 @@
 }
 
 std::unique_ptr<base::Value> PropType::ToJson(bool full_schema,
-                                               ErrorPtr* error) const {
+                                              ErrorPtr* error) const {
   if (!full_schema && !HasOverriddenAttributes()) {
     if (based_on_schema_)
       return std::unique_ptr<base::Value>(new base::DictionaryValue);
-    return TypedValueToJson(GetTypeAsString());
+    return TypedValueToJson(GetTypeAsString(), error);
   }
 
   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
@@ -82,19 +82,19 @@
 }
 
 bool PropType::FromJson(const base::DictionaryValue* value,
-                        const PropType* schema, ErrorPtr* error) {
-  if (schema && schema->GetType() != GetType()) {
+                        const PropType* base_schema, ErrorPtr* error) {
+  if (base_schema && base_schema->GetType() != GetType()) {
     Error::AddToPrintf(error, commands::errors::kDomain,
                        commands::errors::kPropTypeChanged,
                        "Redefining a command of type %s as %s",
-                       schema->GetTypeAsString().c_str(),
+                       base_schema->GetTypeAsString().c_str(),
                        GetTypeAsString().c_str());
     return false;
   }
-  based_on_schema_ = (schema != nullptr);
+  based_on_schema_ = (base_schema != nullptr);
   constraints_.clear();
-  if (schema) {
-    for (const auto& pair : schema->GetConstraints()) {
+  if (base_schema) {
+    for (const auto& pair : base_schema->GetConstraints()) {
       std::shared_ptr<Constraint> inherited(pair.second->CloneAsInherited());
       constraints_.insert(std::make_pair(pair.first, inherited));
     }
@@ -121,7 +121,14 @@
   return p != constraints_.end() ? p->second.get() : nullptr;
 }
 
-bool PropType::ValidateConstraints(const Any& value, ErrorPtr* error) const {
+bool PropType::ValidateValue(const base::Value* value, ErrorPtr* error) const {
+  std::shared_ptr<PropValue> val = CreateValue();
+  CHECK(val) << "Failed to create value object";
+  return val->FromJson(value, error) && ValidateConstraints(*val, error);
+}
+
+bool PropType::ValidateConstraints(const PropValue& value,
+                                   ErrorPtr* error) const {
   for (const auto& pair : constraints_) {
     if (!pair.second->Validate(value, error))
       return false;
@@ -135,6 +142,7 @@
     {ValueType::Double,      "number"},
     {ValueType::String,      "string"},
     {ValueType::Boolean,     "boolean"},
+    {ValueType::Object,      "object"},
   };
   return map;
 }
@@ -173,28 +181,17 @@
   case buffet::ValueType::Boolean:
     prop = new BooleanPropType;
     break;
+  case buffet::ValueType::Object:
+    prop = new ObjectPropType;
+    break;
   }
   return std::unique_ptr<PropType>(prop);
 }
 
 template<typename T>
-bool TypedValueFromJson(const base::Value* value_in, T* value_out,
-                        ErrorPtr* error) {
-  if (!TypedValueFromJson(value_in, value_out)) {
-    std::string value_as_string;
-    base::JSONWriter::Write(value_in, &value_as_string);
-    Error::AddToPrintf(
-        error, commands::errors::kDomain, commands::errors::kTypeMismatch,
-        "Unable to convert %s to %s", value_as_string.c_str(),
-        PropType::GetTypeStringFromType(GetValueType<T>()).c_str());
-    return false;
-  }
-  return true;
-}
-
-template<typename T>
 static std::shared_ptr<Constraint> LoadOneOfConstraint(
-    const base::DictionaryValue* value, ErrorPtr* error) {
+    const base::DictionaryValue* value, const ObjectSchema* object_schema,
+    ErrorPtr* error) {
   const base::ListValue* list = nullptr;
   if (!value->GetListWithoutPathExpansion(commands::attributes::kOneOf_Enum,
                                           &list)) {
@@ -206,7 +203,7 @@
   set.reserve(list->GetSize());
   for (const base::Value* item : *list) {
     T val{};
-    if (!TypedValueFromJson(item, &val, error))
+    if (!TypedValueFromJson(item, object_schema, &val, error))
       return std::shared_ptr<Constraint>();
     set.push_back(val);
   }
@@ -216,12 +213,13 @@
 
 template<class ConstraintClass, typename T>
 static std::shared_ptr<Constraint> LoadMinMaxConstraint(
-    const char* dict_key, const base::DictionaryValue* value, ErrorPtr* error) {
+    const char* dict_key, const base::DictionaryValue* value,
+    const ObjectSchema* object_schema, ErrorPtr* error) {
   InheritableAttribute<T> limit;
 
   const base::Value* src_val = nullptr;
   CHECK(value->Get(dict_key, &src_val)) << "Unable to get min/max constraints";
-  if (!TypedValueFromJson(src_val, &limit.value, error))
+  if (!TypedValueFromJson(src_val, object_schema, &limit.value, error))
     return std::shared_ptr<Constraint>();
   limit.is_inherited = false;
 
@@ -230,13 +228,14 @@
 
 template<typename T>
 bool NumericPropTypeBase::FromJsonHelper(const base::DictionaryValue* value,
-                                         const PropType* schema,
+                                         const PropType* base_schema,
                                          ErrorPtr* error) {
-  if (!PropType::FromJson(value, schema, error))
+  if (!PropType::FromJson(value, base_schema, error))
     return false;
 
   if (value->HasKey(commands::attributes::kOneOf_Enum)) {
-    auto constraint = LoadOneOfConstraint<T>(value, error);
+    auto constraint = LoadOneOfConstraint<T>(value, GetObjectSchemaPtr(),
+                                             error);
     if (!constraint)
       return false;
     AddConstraint(constraint);
@@ -245,7 +244,8 @@
   } else {
     if (value->HasKey(commands::attributes::kNumeric_Min)) {
       auto constraint = LoadMinMaxConstraint<ConstraintMin<T>, T>(
-          commands::attributes::kNumeric_Min, value, error);
+          commands::attributes::kNumeric_Min, value, GetObjectSchemaPtr(),
+          error);
       if (!constraint)
         return false;
       AddConstraint(constraint);
@@ -253,7 +253,8 @@
     }
     if (value->HasKey(commands::attributes::kNumeric_Max)) {
       auto constraint = LoadMinMaxConstraint<ConstraintMax<T>, T>(
-          commands::attributes::kNumeric_Max, value, error);
+          commands::attributes::kNumeric_Max, value, GetObjectSchemaPtr(),
+          error);
       if (!constraint)
         return false;
       AddConstraint(constraint);
@@ -271,7 +272,13 @@
 }
 
 std::shared_ptr<PropValue> IntPropType::CreateValue() const {
-  return std::make_shared<IntValue>();
+  return std::make_shared<IntValue>(this);
+}
+
+std::shared_ptr<PropValue> IntPropType::CreateValue(const Any& val) const {
+  auto v = std::make_shared<IntValue>(this);
+  v->SetValue(val.Get<int>());
+  return std::move(v);
 }
 
 // DoublePropType -------------------------------------------------------------
@@ -281,7 +288,13 @@
 }
 
 std::shared_ptr<PropValue> DoublePropType::CreateValue() const {
-  return std::make_shared<DoubleValue>();
+  return std::make_shared<DoubleValue>(this);
+}
+
+std::shared_ptr<PropValue> DoublePropType::CreateValue(const Any& val) const {
+  auto v = std::make_shared<DoubleValue>(this);
+  v->SetValue(val.Get<double>());
+  return std::move(v);
 }
 
 // StringPropType -------------------------------------------------------------
@@ -291,15 +304,23 @@
 }
 
 std::shared_ptr<PropValue> StringPropType::CreateValue() const {
-  return std::make_shared<StringValue>();
+  return std::make_shared<StringValue>(this);
+}
+
+std::shared_ptr<PropValue> StringPropType::CreateValue(const Any& val) const {
+  auto v = std::make_shared<StringValue>(this);
+  v->SetValue(val.Get<std::string>());
+  return std::move(v);
 }
 
 bool StringPropType::FromJson(const base::DictionaryValue* value,
-                              const PropType* schema, ErrorPtr* error) {
-  if (!PropType::FromJson(value, schema, error))
+                              const PropType* base_schema, ErrorPtr* error) {
+  if (!PropType::FromJson(value, base_schema, error))
     return false;
   if (value->HasKey(commands::attributes::kOneOf_Enum)) {
-    auto constraint = LoadOneOfConstraint<std::string>(value, error);
+    auto constraint = LoadOneOfConstraint<std::string>(value,
+                                                       GetObjectSchemaPtr(),
+                                                       error);
     if (!constraint)
       return false;
     AddConstraint(constraint);
@@ -308,7 +329,8 @@
   } else {
     if (value->HasKey(commands::attributes::kString_MinLength)) {
       auto constraint = LoadMinMaxConstraint<ConstraintStringLengthMin, int>(
-          commands::attributes::kString_MinLength, value, error);
+          commands::attributes::kString_MinLength, value, GetObjectSchemaPtr(),
+          error);
       if (!constraint)
         return false;
       AddConstraint(constraint);
@@ -316,7 +338,8 @@
     }
     if (value->HasKey(commands::attributes::kString_MaxLength)) {
       auto constraint = LoadMinMaxConstraint<ConstraintStringLengthMax, int>(
-          commands::attributes::kString_MaxLength, value, error);
+          commands::attributes::kString_MaxLength, value, GetObjectSchemaPtr(),
+          error);
       if (!constraint)
         return false;
       AddConstraint(constraint);
@@ -352,16 +375,111 @@
 }
 
 std::shared_ptr<PropValue> BooleanPropType::CreateValue() const {
-  return std::make_shared<BooleanValue>();
+  return std::make_shared<BooleanValue>(this);
+}
+
+std::shared_ptr<PropValue> BooleanPropType::CreateValue(const Any& val) const {
+  auto v = std::make_shared<BooleanValue>(this);
+  v->SetValue(val.Get<bool>());
+  return std::move(v);
 }
 
 bool BooleanPropType::FromJson(const base::DictionaryValue* value,
-                               const PropType* schema, ErrorPtr* error) {
-  if (!PropType::FromJson(value, schema, error))
+                               const PropType* base_schema, ErrorPtr* error) {
+  if (!PropType::FromJson(value, base_schema, error))
     return false;
 
   if (value->HasKey(commands::attributes::kOneOf_Enum)) {
-    auto constraint = LoadOneOfConstraint<bool>(value, error);
+    auto constraint = LoadOneOfConstraint<bool>(value, GetObjectSchemaPtr(),
+                                                error);
+    if (!constraint)
+      return false;
+    AddConstraint(constraint);
+  }
+
+  return true;
+}
+
+// ObjectPropType -------------------------------------------------------------
+
+ObjectPropType::ObjectPropType()
+    : object_schema_(std::make_shared<ObjectSchema>(), false) {}
+
+std::shared_ptr<PropType> ObjectPropType::Clone() const {
+  return std::make_shared<ObjectPropType>(*this);
+}
+
+std::shared_ptr<PropValue> ObjectPropType::CreateValue() const {
+  return std::make_shared<ObjectValue>(this);
+}
+
+std::shared_ptr<PropValue> ObjectPropType::CreateValue(const Any& val) const {
+  auto v = std::make_shared<ObjectValue>(this);
+  v->SetValue(val.Get<native_types::Object>());
+  return std::move(v);
+}
+
+bool ObjectPropType::HasOverriddenAttributes() const {
+  return PropType::HasOverriddenAttributes() ||
+         !object_schema_.is_inherited;
+}
+
+
+std::unique_ptr<base::Value> ObjectPropType::ToJson(bool full_schema,
+                                                    ErrorPtr* error) const {
+  std::unique_ptr<base::Value> value = PropType::ToJson(full_schema, error);
+  if (value) {
+    base::DictionaryValue* dict = nullptr;
+    CHECK(value->GetAsDictionary(&dict)) << "Expecting a JSON object";
+    if (!object_schema_.is_inherited || full_schema) {
+      auto object_schema = object_schema_.value->ToJson(full_schema, error);
+      if (!object_schema) {
+        value.reset();
+        return value;
+      }
+      dict->SetWithoutPathExpansion(commands::attributes::kObject_Properties,
+                                    object_schema.release());
+    }
+  }
+  return value;
+}
+
+bool ObjectPropType::FromJson(const base::DictionaryValue* value,
+                              const PropType* base_schema, ErrorPtr* error) {
+  if (!PropType::FromJson(value, base_schema, error))
+    return false;
+
+  using commands::attributes::kObject_Properties;
+
+  std::shared_ptr<const ObjectSchema> base_object_schema;
+  if (base_schema)
+    base_object_schema = base_schema->GetObject()->GetObjectSchema();
+
+  const base::DictionaryValue* props = nullptr;
+  if (value->GetDictionaryWithoutPathExpansion(kObject_Properties, &props)) {
+    auto object_schema = std::make_shared<ObjectSchema>();
+    if (!object_schema->FromJson(props, base_object_schema.get(), error)) {
+      Error::AddTo(error, commands::errors::kDomain,
+                   commands::errors::kInvalidObjectSchema,
+                   "Error parsing object property schema");
+      return false;
+    }
+    object_schema_.value = object_schema;
+    object_schema_.is_inherited = false;
+  } else if (base_object_schema) {
+    object_schema_.value = base_object_schema;
+    object_schema_.is_inherited = true;
+  } else {
+    Error::AddToPrintf(error, commands::errors::kDomain,
+                       commands::errors::kInvalidObjectSchema,
+                       "Object type definition must include the object schema "
+                       "('%s' field not found)", kObject_Properties);
+    return false;
+  }
+
+  if (value->HasKey(commands::attributes::kOneOf_Enum)) {
+    auto constraint = LoadOneOfConstraint<native_types::Object>(
+        value, GetObjectSchemaPtr(), error);
     if (!constraint)
       return false;
     AddConstraint(constraint);
diff --git a/buffet/commands/prop_types.h b/buffet/commands/prop_types.h
index 63d3c44..e39b356 100644
--- a/buffet/commands/prop_types.h
+++ b/buffet/commands/prop_types.h
@@ -18,10 +18,11 @@
 
 namespace buffet {
 
-// Helper function to read in a C++ type from JSON value.
-template<typename T>
-bool TypedValueFromJson(const base::Value* value_in, T* value_out,
-                        ErrorPtr* error);
+class IntPropType;
+class DoublePropType;
+class StringPropType;
+class BooleanPropType;
+class ObjectPropType;
 
 // PropType is a base class for all parameter type definition objects.
 // Property definitions of a particular type will derive from this class and
@@ -70,12 +71,15 @@
   virtual StringPropType const* GetString() const { return nullptr; }
   virtual BooleanPropType* GetBoolean() { return nullptr; }
   virtual BooleanPropType const* GetBoolean() const { return nullptr; }
+  virtual ObjectPropType* GetObject() { return nullptr; }
+  virtual ObjectPropType const* GetObject() const { return nullptr; }
 
   // Makes a full copy of this type definition.
   virtual std::shared_ptr<PropType> Clone() const = 0;
   // Creates an instance of associated value object, using the parameter
   // type as a factory class.
   virtual std::shared_ptr<PropValue> CreateValue() const = 0;
+  virtual std::shared_ptr<PropValue> CreateValue(const Any& val) const = 0;
 
   // Saves the parameter type definition as a JSON object.
   // If |full_schema| is set to true, the full type definition is saved,
@@ -86,22 +90,21 @@
   // error information.
   virtual std::unique_ptr<base::Value> ToJson(bool full_schema,
                                               ErrorPtr* error) const;
-  // Parses an JSON parameter type definition. Optional |schema| may specify
-  // the base schema type definition this type should be based upon.
+  // Parses an JSON parameter type definition. Optional |base_schema| may
+  // specify the base schema type definition this type should be based upon.
   // If not specified (nullptr), the parameter type is assumed to be a full
   // definition and any omitted required properties are treated as an error.
   // Returns true on success, otherwise fills in the |error| with additional
   // error information.
   virtual bool FromJson(const base::DictionaryValue* value,
-                        const PropType* schema, ErrorPtr* error);
+                        const PropType* base_schema, ErrorPtr* error);
 
   // Validates a JSON value for the parameter type to make sure it satisfies
   // the parameter type definition including any specified constraints.
   // Returns false if the |value| does not meet the requirements of the type
   // definition and returns additional information about the failure via
   // the |error| parameter.
-  virtual bool ValidateValue(const base::Value* value,
-                             ErrorPtr* error) const = 0;
+  bool ValidateValue(const base::Value* value, ErrorPtr* error) const;
 
   // Additional helper static methods to help with converting a type enum
   // value into a string and back.
@@ -126,11 +129,19 @@
   const Constraint* GetConstraint(ConstraintType constraint_type) const;
   Constraint* GetConstraint(ConstraintType constraint_type);
 
- protected:
-  // Validates the given value against all the constraints.
-  // This is a helper method used by PropType::ValidateValue().
-  bool ValidateConstraints(const Any& value, ErrorPtr* error) const;
+  // Returns a schema for Object-type parameter. This will be nullptr for
+  // every type but Object.
+  const ObjectSchema* GetObjectSchemaPtr() const {
+    return GetObjectSchema().get();
+  }
+  virtual std::shared_ptr<const ObjectSchema> GetObjectSchema() const {
+    return std::shared_ptr<const ObjectSchema>();
+  }
 
+  // Validates the given value against all the constraints.
+  bool ValidateConstraints(const PropValue& value, ErrorPtr* error) const;
+
+ protected:
   // Helper method to obtaining a vector of OneOf constraint values.
   template<typename T>
   std::vector<T> GetOneOfValuesHelper() const {
@@ -138,13 +149,6 @@
         GetConstraint(ConstraintType::OneOf));
     return ofc ? ofc->set_.value : std::vector<T>();
   }
-  // Helper methods to validating parameter values for various types.
-  template<typename T>
-  bool ValidateValueHelper(const base::Value* value, ErrorPtr* error) const {
-    T val;
-    return TypedValueFromJson(value, &val, error) &&
-           ValidateConstraints(val, error);
-  }
 
   // Specifies if this parameter definition is derived from a base
   // object schema.
@@ -190,7 +194,7 @@
   // Helper method for implementing FromJson in derived classes.
   template<typename T>
   bool FromJsonHelper(const base::DictionaryValue* value,
-                      const PropType* schema, ErrorPtr* error);
+                      const PropType* base_schema, ErrorPtr* error);
 };
 
 // Property definition of Integer type.
@@ -204,15 +208,11 @@
 
   virtual std::shared_ptr<PropType> Clone() const override;
   virtual std::shared_ptr<PropValue> CreateValue() const override;
+  virtual std::shared_ptr<PropValue> CreateValue(const Any& val) const override;
 
   virtual bool FromJson(const base::DictionaryValue* value,
-                        const PropType* schema, ErrorPtr* error) override {
-    return FromJsonHelper<int>(value, schema, error);
-  }
-
-  virtual bool ValidateValue(const base::Value* value,
-                             ErrorPtr* error) const override {
-    return ValidateValueHelper<int>(value, error);
+                        const PropType* base_schema, ErrorPtr* error) override {
+    return FromJsonHelper<int>(value, base_schema, error);
   }
 
   // Helper methods to add and inspect simple constraints.
@@ -238,15 +238,11 @@
 
   virtual std::shared_ptr<PropType> Clone() const override;
   virtual std::shared_ptr<PropValue> CreateValue() const override;
+  virtual std::shared_ptr<PropValue> CreateValue(const Any& val) const override;
 
   virtual bool FromJson(const base::DictionaryValue* value,
-                        const PropType* schema, ErrorPtr* error) override {
-    return FromJsonHelper<double>(value, schema, error);
-  }
-
-  virtual bool ValidateValue(const base::Value* value,
-                             ErrorPtr* error) const override {
-    return ValidateValueHelper<double>(value, error);
+                        const PropType* base_schema, ErrorPtr* error) override {
+    return FromJsonHelper<double>(value, base_schema, error);
   }
 
   // Helper methods to add and inspect simple constraints.
@@ -272,14 +268,10 @@
 
   virtual std::shared_ptr<PropType> Clone() const override;
   virtual std::shared_ptr<PropValue> CreateValue() const override;
+  virtual std::shared_ptr<PropValue> CreateValue(const Any& val) const override;
 
   virtual bool FromJson(const base::DictionaryValue* value,
-                        const PropType* schema, ErrorPtr* error) override;
-
-  virtual bool ValidateValue(const base::Value* value,
-                             ErrorPtr* error) const override {
-    return ValidateValueHelper<std::string>(value, error);
-  }
+                        const PropType* base_schema, ErrorPtr* error) override;
 
   // Helper methods to add and inspect simple constraints.
   // Used mostly for unit testing.
@@ -302,14 +294,10 @@
 
   virtual std::shared_ptr<PropType> Clone() const override;
   virtual std::shared_ptr<PropValue> CreateValue() const override;
+  virtual std::shared_ptr<PropValue> CreateValue(const Any& val) const override;
 
   virtual bool FromJson(const base::DictionaryValue* value,
-                        const PropType* schema, ErrorPtr* error) override;
-
-  virtual bool ValidateValue(const base::Value* value,
-                             ErrorPtr* error) const override {
-    return ValidateValueHelper<bool>(value, error);
-  }
+                        const PropType* base_schema, ErrorPtr* error) override;
 
   // Helper methods to add and inspect simple constraints.
   // Used mostly for unit testing.
@@ -318,6 +306,38 @@
   }
 };
 
+// Parameter definition of Object type.
+class ObjectPropType : public PropType {
+ public:
+  ObjectPropType();
+
+  // Overrides from the ParamType base class.
+  virtual ValueType GetType() const override { return ValueType::Object; }
+  virtual bool HasOverriddenAttributes() const override;
+
+  virtual ObjectPropType* GetObject() override { return this; }
+  virtual ObjectPropType const* GetObject() const override { return this; }
+
+  virtual std::shared_ptr<PropType> Clone() const override;
+  virtual std::shared_ptr<PropValue> CreateValue() const override;
+  virtual std::shared_ptr<PropValue> CreateValue(const Any& val) const override;
+
+  virtual std::unique_ptr<base::Value> ToJson(bool full_schema,
+                                              ErrorPtr* error) const override;
+  virtual bool FromJson(const base::DictionaryValue* value,
+                        const PropType* base_schema, ErrorPtr* error) override;
+
+  virtual std::shared_ptr<const ObjectSchema> GetObjectSchema() const override {
+    return object_schema_.value;
+  }
+  void SetObjectSchema(const std::shared_ptr<const ObjectSchema>& schema) {
+    object_schema_.value = schema;
+    object_schema_.is_inherited = false;
+  }
+
+ private:
+  InheritableAttribute<std::shared_ptr<const ObjectSchema>> object_schema_;
+};
 }  // namespace buffet
 
 #endif  // BUFFET_COMMANDS_PROP_TYPES_H_
diff --git a/buffet/commands/prop_values.cc b/buffet/commands/prop_values.cc
index e2eabc0..0c98a20 100644
--- a/buffet/commands/prop_values.cc
+++ b/buffet/commands/prop_values.cc
@@ -2,22 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(avakulenko) Remove this file by Aug 1, 2014 if nothing ends up here...
+
 #include "buffet/commands/prop_values.h"
 
-#include <memory>
-
-#include <base/values.h>
-
-#include "buffet/commands/prop_types.h"
-
 namespace buffet {
 
-// Specializations of generic GetValueType<T>() for supported C++ types.
-template<> ValueType GetValueType<int>() { return ValueType::Int; }
-template<> ValueType GetValueType<double>() { return ValueType::Double; }
-template<> ValueType GetValueType<std::string>() { return ValueType::String; }
-template<> ValueType GetValueType<bool>() { return ValueType::Boolean; }
-
-PropValue::~PropValue() {}
 
 }  // namespace buffet
diff --git a/buffet/commands/prop_values.h b/buffet/commands/prop_values.h
index e03d39b..adaa91d 100644
--- a/buffet/commands/prop_values.h
+++ b/buffet/commands/prop_values.h
@@ -9,7 +9,8 @@
 #include <memory>
 #include <string>
 
-#include "buffet/commands/prop_constraints.h"
+#include "buffet/any.h"
+#include "buffet/commands/schema_utils.h"
 #include "buffet/error.h"
 
 namespace base {
@@ -24,38 +25,52 @@
   Int,
   Double,
   String,
-  Boolean
+  Boolean,
+  Object
 };
 
+class PropValue;
+class IntValue;
+class DoubleValue;
+class StringValue;
+class BooleanValue;
+class ObjectValue;
+
+class PropType;
+
 // Helper methods to get the parameter type enum value for the given
 // native C++ data representation.
 // The generic GetValueType<T>() is undefined, however particular
 // type specializations return the appropriate ValueType.
 template<typename T> ValueType GetValueType();  // Undefined.
-template<> ValueType GetValueType<int>();
-template<> ValueType GetValueType<double>();
-template<> ValueType GetValueType<std::string>();
-template<> ValueType GetValueType<bool>();
+template<>
+inline ValueType GetValueType<int>() { return ValueType::Int; }
+template<>
+inline ValueType GetValueType<double>() { return ValueType::Double; }
+template<>
+inline ValueType GetValueType<std::string>() { return ValueType::String; }
+template<>
+inline ValueType GetValueType<bool>() { return ValueType::Boolean; }
+template<>
+inline ValueType GetValueType<native_types::Object>() {
+  return ValueType::Object;
+}
 
-class ObjectSchema;
-
-class PropValue;
-class IntPropType;
-class DoublePropType;
-class StringPropType;
-class BooleanPropType;
-
-class IntValue;
-class DoubleValue;
-class StringValue;
-class BooleanValue;
-
-// The base class for parameter values.
+// The base class for property values.
 // Concrete value classes of various types will be derived from this base.
+// A property value is the actual command parameter value (or a concrete value
+// that can be used in constraints and presets). The PropValue is mostly
+// just parsed content of base::Value when a command is dispatched, however
+// it does have some additional functionality:
+//   - it has a reference to the type definition (PropType) which is used
+//     when validating the value, especially for "object" types.
+//   - it can be compared with another instances of values of the same type.
+//     This is used to validate the values against "enum"/"one of" constraints.
 class PropValue {
  public:
-  PropValue() = default;
-  virtual ~PropValue();
+  explicit PropValue(const PropType* type)
+      : type_(type) {}
+  virtual ~PropValue() = default;
 
   // Gets the type of the value.
   virtual ValueType GetType() const = 0;
@@ -69,6 +84,8 @@
   virtual StringValue const* GetString() const { return nullptr; }
   virtual BooleanValue* GetBoolean() { return nullptr; }
   virtual BooleanValue const* GetBoolean() const { return nullptr; }
+  virtual ObjectValue* GetObject() { return nullptr; }
+  virtual ObjectValue const* GetObject() const { return nullptr; }
 
   // Makes a full copy of this value class.
   virtual std::shared_ptr<PropValue> Clone() const = 0;
@@ -80,7 +97,19 @@
   // Parses a value from JSON.
   // If it fails, it returns false and provides additional information
   // via the |error| parameter.
-  virtual bool FromJson(const base::Value* value, ErrorPtr* error) = 0;
+  virtual bool FromJson(const base::Value* value,
+                        ErrorPtr* error) = 0;
+
+  // Returns the contained C++ value as Any.
+  virtual Any GetValueAsAny() const = 0;
+
+  // Return the type definition of this value.
+  const PropType* GetPropType() const { return type_; }
+  // Compares two values and returns true if they are equal.
+  virtual bool IsEqual(const PropValue* value) const = 0;
+
+ protected:
+  const PropType* type_;  // weak pointer
 };
 
 // A helper template base class for implementing simple (non-Object) value
@@ -88,6 +117,12 @@
 template<typename Derived, typename T>
 class TypedValueBase : public PropValue {
  public:
+  // To help refer to this base class from derived classes, define _Base to
+  // be this class.
+  using _Base = TypedValueBase<Derived, T>;
+  // Expose the non-default constructor of the base class.
+  using PropValue::PropValue;
+
   // Overrides from PropValue base class.
   virtual ValueType GetType() const override { return GetValueType<T>(); }
   virtual std::shared_ptr<PropValue> Clone() const override {
@@ -95,15 +130,24 @@
   }
 
   virtual std::unique_ptr<base::Value> ToJson(ErrorPtr* error) const override {
-    (void)error;  // unused.
-    return TypedValueToJson(value_);
+    return TypedValueToJson(value_, error);
   }
 
-  virtual bool FromJson(const base::Value* value, ErrorPtr* error) override {
-    return TypedValueFromJson(value, &value_, error);
+  virtual bool FromJson(const base::Value* value,
+                        ErrorPtr* error) override {
+    return TypedValueFromJson(value, GetPropType()->GetObjectSchemaPtr(),
+                              &value_, error);
   }
 
-  // Helper method to get and set the C++ representation of the value.
+  virtual bool IsEqual(const PropValue* value) const override {
+    if (GetType() != value->GetType())
+      return false;
+    const _Base* value_base = static_cast<const _Base*>(value);
+    return CompareValue(GetValue(), value_base->GetValue());
+  }
+
+  // Helper methods to get and set the C++ representation of the value.
+  virtual Any GetValueAsAny() const override { return value_; }
   const T& GetValue() const { return value_; }
   void SetValue(T value) { value_ = std::move(value); }
 
@@ -114,6 +158,7 @@
 // Value of type Integer.
 class IntValue final : public TypedValueBase<IntValue, int> {
  public:
+  using _Base::_Base;  // Expose the custom constructor of the base class.
   virtual IntValue* GetInt() override { return this; }
   virtual IntValue const* GetInt() const override { return this; }
 };
@@ -121,6 +166,7 @@
 // Value of type Number.
 class DoubleValue final : public TypedValueBase<DoubleValue, double> {
  public:
+  using _Base::_Base;  // Expose the custom constructor of the base class.
   virtual DoubleValue* GetDouble() override { return this; }
   virtual DoubleValue const* GetDouble() const override { return this; }
 };
@@ -128,6 +174,7 @@
 // Value of type String.
 class StringValue final : public TypedValueBase<StringValue, std::string> {
  public:
+  using _Base::_Base;  // Expose the custom constructor of the base class.
   virtual StringValue* GetString() override { return this; }
   virtual StringValue const* GetString() const override { return this; }
 };
@@ -135,10 +182,19 @@
 // Value of type Boolean.
 class BooleanValue final : public TypedValueBase<BooleanValue, bool> {
  public:
+  using _Base::_Base;  // Expose the custom constructor of the base class.
   virtual BooleanValue* GetBoolean() override { return this; }
   virtual BooleanValue const* GetBoolean() const override { return this; }
 };
 
+// Value of type Object.
+class ObjectValue final : public TypedValueBase<ObjectValue,
+                                                native_types::Object> {
+ public:
+  using _Base::_Base;  // Expose the custom constructor of the base class.
+  virtual ObjectValue* GetObject() override { return this; }
+  virtual ObjectValue const* GetObject() const override { return this; }
+};
 }  // namespace buffet
 
 #endif  // BUFFET_COMMANDS_PROP_VALUES_H_
diff --git a/buffet/commands/schema_constants.cc b/buffet/commands/schema_constants.cc
index 15d6db3..d8ec689 100644
--- a/buffet/commands/schema_constants.cc
+++ b/buffet/commands/schema_constants.cc
@@ -8,28 +8,35 @@
 namespace commands {
 
 namespace errors {
-const char kDomain[]            = "command_schema";
+const char kDomain[] = "command_schema";
 
-const char kOutOfRange[]        = "out_of_range";
-const char kTypeMismatch[]      = "type_mismatch";
-const char kPropTypeChanged[]   = "param_type_changed";
-const char kUnknownType[]       = "unknown_type";
-const char kInvalidPropDef[]    = "invalid_parameter_definition";
-const char kNoTypeInfo[]        = "no_type_info";
-const char kPropertyMissing[]   = "parameter_missing";
+const char kOutOfRange[] = "out_of_range";
+const char kTypeMismatch[] = "type_mismatch";
+const char kPropTypeChanged[] = "param_type_changed";
+const char kUnknownType[] = "unknown_type";
+const char kInvalidPropDef[] = "invalid_parameter_definition";
+const char kInvalidPropValue[] = "invalid_parameter_value";
+const char kNoTypeInfo[] = "no_type_info";
+const char kPropertyMissing[] = "parameter_missing";
+const char kUnknownProperty[] = "unexpected_parameter";
+const char kInvalidObjectSchema[] = "invalid_object_schema";
 }  // namespace errors
 
 namespace attributes {
-const char kType[]              = "type";
-const char kDisplayName[]       = "displayName";
+const char kType[] = "type";
+const char kDisplayName[] = "displayName";
 
-const char kNumeric_Min[]       = "minimum";
-const char kNumeric_Max[]       = "maximum";
+const char kNumeric_Min[] = "minimum";
+const char kNumeric_Max[] = "maximum";
 
-const char kString_MinLength[]  = "minLength";
-const char kString_MaxLength[]  = "maxLength";
+const char kString_MinLength[] = "minLength";
+const char kString_MaxLength[] = "maxLength";
 
-const char kOneOf_Enum[]        = "enum";
+const char kOneOf_Enum[] = "enum";
+const char kOneOf_Metadata[] = "metadata";
+const char kOneOf_MetaSchema[] = "schema";
+
+const char kObject_Properties[] = "properties";
 }  // namespace attributes
 
 }  // namespace commands
diff --git a/buffet/commands/schema_constants.h b/buffet/commands/schema_constants.h
index 0e58cff..087f53b 100644
--- a/buffet/commands/schema_constants.h
+++ b/buffet/commands/schema_constants.h
@@ -18,8 +18,11 @@
 extern const char kPropTypeChanged[];
 extern const char kUnknownType[];
 extern const char kInvalidPropDef[];
+extern const char kInvalidPropValue[];
 extern const char kNoTypeInfo[];
 extern const char kPropertyMissing[];
+extern const char kUnknownProperty[];
+extern const char kInvalidObjectSchema[];
 }  // namespace errors
 
 namespace attributes {
@@ -34,6 +37,10 @@
 extern const char kString_MaxLength[];
 
 extern const char kOneOf_Enum[];
+extern const char kOneOf_Metadata[];
+extern const char kOneOf_MetaSchema[];
+
+extern const char kObject_Properties[];
 }  // namespace attributes
 
 }  // namespace commands
diff --git a/buffet/commands/schema_utils.cc b/buffet/commands/schema_utils.cc
new file mode 100644
index 0000000..3c6ae57
--- /dev/null
+++ b/buffet/commands/schema_utils.cc
@@ -0,0 +1,191 @@
+// Copyright 2014 The Chromium OS 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 "buffet/commands/schema_utils.h"
+
+#include <algorithm>
+#include <set>
+#include <string>
+
+#include <base/json/json_writer.h>
+
+#include "buffet/commands/object_schema.h"
+#include "buffet/commands/prop_types.h"
+#include "buffet/commands/prop_values.h"
+
+namespace buffet {
+namespace {
+// Helper function to report "type mismatch" errors when parsing JSON.
+void ReportJsonTypeMismatch(const base::Value* value_in,
+                            const std::string& expected_type,
+                            ErrorPtr* error) {
+  std::string value_as_string;
+  base::JSONWriter::Write(value_in, &value_as_string);
+  Error::AddToPrintf(error, commands::errors::kDomain,
+                     commands::errors::kTypeMismatch,
+                     "Unable to convert value %s into %s",
+                     value_as_string.c_str(), expected_type.c_str());
+}
+
+// Template version of ReportJsonTypeMismatch that deduces the type of expected
+// data from the value_out parameter passed to particular overload of
+// TypedValueFromJson() function. Always returns false.
+template<typename T>
+bool ReportUnexpectedJson(const base::Value* value_in, T*, ErrorPtr* error) {
+  ReportJsonTypeMismatch(value_in,
+                         PropType::GetTypeStringFromType(GetValueType<T>()),
+                         error);
+  return false;
+}
+
+bool ErrorMissingProperty(ErrorPtr* error, const char* param_name) {
+  Error::AddToPrintf(error, commands::errors::kDomain,
+                     commands::errors::kPropertyMissing,
+                     "Required parameter missing: %s", param_name);
+  return false;
+}
+}  // namespace
+
+// Specializations of TypedValueToJson<T>() for supported C++ types.
+std::unique_ptr<base::Value> TypedValueToJson(bool value, ErrorPtr* error) {
+  return std::unique_ptr<base::Value>(base::Value::CreateBooleanValue(value));
+}
+
+std::unique_ptr<base::Value> TypedValueToJson(int value, ErrorPtr* error) {
+  return std::unique_ptr<base::Value>(base::Value::CreateIntegerValue(value));
+}
+
+std::unique_ptr<base::Value> TypedValueToJson(double value, ErrorPtr* error) {
+  return std::unique_ptr<base::Value>(base::Value::CreateDoubleValue(value));
+}
+
+std::unique_ptr<base::Value> TypedValueToJson(const std::string& value,
+                                              ErrorPtr* error) {
+  return std::unique_ptr<base::Value>(base::Value::CreateStringValue(value));
+}
+
+std::unique_ptr<base::Value> TypedValueToJson(const native_types::Object& value,
+                                              ErrorPtr* error) {
+  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+  for (const auto& pair : value) {
+    auto prop_value = pair.second->ToJson(error);
+    if (!prop_value)
+      return prop_value;
+    dict->SetWithoutPathExpansion(pair.first, prop_value.release());
+  }
+  return std::move(dict);
+}
+
+bool TypedValueFromJson(const base::Value* value_in,
+                        const ObjectSchema* object_schema,
+                        bool* value_out, ErrorPtr* error) {
+  return value_in->GetAsBoolean(value_out) ||
+         ReportUnexpectedJson(value_in, value_out, error);
+}
+
+bool TypedValueFromJson(const base::Value* value_in,
+                        const ObjectSchema* object_schema,
+                        int* value_out, ErrorPtr* error) {
+  return value_in->GetAsInteger(value_out) ||
+         ReportUnexpectedJson(value_in, value_out, error);
+}
+
+bool TypedValueFromJson(const base::Value* value_in,
+                        const ObjectSchema* object_schema,
+                        double* value_out, ErrorPtr* error) {
+  return value_in->GetAsDouble(value_out) ||
+         ReportUnexpectedJson(value_in, value_out, error);
+}
+
+bool TypedValueFromJson(const base::Value* value_in,
+                        const ObjectSchema* object_schema,
+                        std::string* value_out, ErrorPtr* error) {
+  return value_in->GetAsString(value_out) ||
+         ReportUnexpectedJson(value_in, value_out, error);
+}
+
+bool TypedValueFromJson(const base::Value* value_in,
+                        const ObjectSchema* object_schema,
+                        native_types::Object* value_out, ErrorPtr* error) {
+  const base::DictionaryValue* dict = nullptr;
+  if (!value_in->GetAsDictionary(&dict))
+    return ReportUnexpectedJson(value_in, value_out, error);
+
+  CHECK(object_schema) << "Object schema must be provided";
+
+  std::set<std::string> keys_processed;
+  for (const auto& pair : object_schema->GetProps()) {
+    const PropValue* def_value = pair.second->GetDefaultValue();
+    if (dict->HasKey(pair.first)) {
+      std::shared_ptr<PropValue> value = pair.second->CreateValue();
+      const base::Value* param_value = nullptr;
+      CHECK(dict->GetWithoutPathExpansion(pair.first, &param_value))
+          << "Unable to get parameter";
+      if (!value->FromJson(param_value, error))
+        return false;
+      value_out->insert(std::make_pair(pair.first, std::move(value)));
+    } else if (def_value) {
+      std::shared_ptr<PropValue> value = def_value->Clone();
+      value_out->insert(std::make_pair(pair.first, std::move(value)));
+    } else {
+      return ErrorMissingProperty(error, pair.first.c_str());
+    }
+    keys_processed.insert(pair.first);
+  }
+
+  // Just for sanity, make sure that we processed all the necessary properties
+  // and there weren't any extra (unknown) ones specified. If so, ignore
+  // them, but log as warnings...
+  base::DictionaryValue::Iterator iter(*dict);
+  while (!iter.IsAtEnd()) {
+    std::string key = iter.key();
+    if (keys_processed.find(key) == keys_processed.end() &&
+        !object_schema->GetExtraPropertiesAllowed()) {
+      Error::AddToPrintf(error, commands::errors::kDomain,
+                         commands::errors::kUnknownProperty,
+                         "Unrecognized parameter '%s'", key.c_str());
+      return false;
+    }
+    iter.Advance();
+  }
+
+  // Now go over all property values and validate them.
+  for (const auto& pair : *value_out) {
+    const PropType* prop_type = pair.second->GetPropType();
+    CHECK(prop_type) << "Value property type must be available";
+    if (!prop_type->ValidateConstraints(*pair.second, error)) {
+      Error::AddToPrintf(error, commands::errors::kDomain,
+                         commands::errors::kInvalidPropValue,
+                         "Invalid parameter value for property '%s'",
+                         pair.first.c_str());
+      return false;
+    }
+  }
+  return true;
+}
+
+// Compares two sets of key-value pairs from two Objects.
+static bool obj_cmp(const native_types::Object::value_type& v1,
+                    const native_types::Object::value_type& v2) {
+  return (v1.first == v2.first) && v1.second->IsEqual(v2.second.get());
+}
+
+bool operator==(const native_types::Object& obj1,
+                const native_types::Object& obj2) {
+  if (obj1.size() != obj2.size())
+    return false;
+
+  auto pair = std::mismatch(obj1.begin(), obj1.end(), obj2.begin(), obj_cmp);
+  return pair == std::make_pair(obj1.end(), obj2.end());
+}
+
+std::string ToString(const native_types::Object& obj) {
+  auto val = TypedValueToJson(obj, nullptr);
+  std::string str;
+  base::JSONWriter::Write(val.get(), &str);
+  return str;
+}
+
+
+}  // namespace buffet
diff --git a/buffet/commands/schema_utils.h b/buffet/commands/schema_utils.h
new file mode 100644
index 0000000..ff9d461
--- /dev/null
+++ b/buffet/commands/schema_utils.h
@@ -0,0 +1,118 @@
+// Copyright 2014 The Chromium OS 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 BUFFET_COMMANDS_SCHEMA_UTILS_H_
+#define BUFFET_COMMANDS_SCHEMA_UTILS_H_
+
+#include <limits>
+#include <map>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <base/values.h>
+
+#include <buffet/error.h>
+
+namespace buffet {
+
+class PropValue;
+class ObjectSchema;
+
+namespace native_types {
+// C++ representation of object values.
+using Object = std::map<std::string, std::shared_ptr<PropValue>>;
+}  // namespace native_types
+// Converts an object to string.
+std::string ToString(const native_types::Object& obj);
+
+// InheritableAttribute class is used for specifying various command parameter
+// attributes that can be inherited from a base (parent) schema.
+// The |value| still specifies the actual attribute values, whether it
+// is inherited or overridden, while |is_inherited| can be used to identify
+// if the attribute was inherited (true) or overridden (false).
+template<typename T>
+class InheritableAttribute {
+ public:
+  InheritableAttribute() = default;
+  explicit InheritableAttribute(T val)
+      : value(std::move(val)), is_inherited(true) {}
+  InheritableAttribute(T val, bool inherited)
+      : value(std::move(val)), is_inherited(inherited) {}
+  T value{};
+  bool is_inherited{true};
+};
+
+// A bunch of helper function to create base::Value for specific C++ classes,
+// including vectors of types. These are used in template classes below
+// to simplify specialization logic.
+std::unique_ptr<base::Value> TypedValueToJson(bool value, ErrorPtr* error);
+std::unique_ptr<base::Value> TypedValueToJson(int value, ErrorPtr* error);
+std::unique_ptr<base::Value> TypedValueToJson(double value, ErrorPtr* error);
+std::unique_ptr<base::Value> TypedValueToJson(const std::string& value,
+                                              ErrorPtr* error);
+std::unique_ptr<base::Value> TypedValueToJson(const native_types::Object& value,
+                                              ErrorPtr* error);
+template<typename T>
+std::unique_ptr<base::Value> TypedValueToJson(const std::vector<T>& values,
+                                              ErrorPtr* error) {
+  std::unique_ptr<base::ListValue> list(new base::ListValue);
+  for (const auto& v : values) {
+    auto json = TypedValueToJson(v, error);
+    if (!json)
+      return std::unique_ptr<base::Value>();
+    list->Append(json.release());
+  }
+  return std::move(list);
+}
+
+// Similarly to CreateTypedValue() function above, the following overloaded
+// helper methods allow to extract specific C++ data types from base::Value.
+// Also used in template classes below to simplify specialization logic.
+bool TypedValueFromJson(const base::Value* value_in,
+                        const ObjectSchema* object_schema,
+                        bool* value_out, ErrorPtr* error);
+bool TypedValueFromJson(const base::Value* value_in,
+                        const ObjectSchema* object_schema,
+                        int* value_out, ErrorPtr* error);
+bool TypedValueFromJson(const base::Value* value_in,
+                        const ObjectSchema* object_schema,
+                        double* value_out, ErrorPtr* error);
+bool TypedValueFromJson(const base::Value* value_in,
+                        const ObjectSchema* object_schema,
+                        std::string* value_out, ErrorPtr* error);
+bool TypedValueFromJson(const base::Value* value_in,
+                        const ObjectSchema* object_schema,
+                        native_types::Object* value_out, ErrorPtr* error);
+
+bool operator==(const native_types::Object& obj1,
+                const native_types::Object& obj2);
+
+// CompareValue is a helper function to help with implementing EqualsTo operator
+// for various data types. For most scalar types it is using operator==(),
+// however, for floating point values, rounding errors in binary representation
+// of IEEE floats/doubles can cause straight == comparison to fail for seemingly
+// equivalent values. For these, use approximate comparison with the error
+// margin equal to the epsilon value defined for the corresponding data type.
+// This is used when looking up values for implementation of OneOf constraints
+// which should work reliably for floating points also ("number" type).
+
+// Compare exact types using ==.
+template<typename T>
+inline typename std::enable_if<!std::is_floating_point<T>::value, bool>::type
+CompareValue(const T& v1, const T& v2) {
+  return v1 == v2;
+}
+
+// Compare non-exact types (such as double) using precision margin (epsilon).
+template<typename T>
+inline typename std::enable_if<std::is_floating_point<T>::value, bool>::type
+CompareValue(const T& v1, const T& v2) {
+  return std::abs(v1 - v2) <= std::numeric_limits<T>::epsilon();
+}
+
+}  // namespace buffet
+
+#endif  // BUFFET_COMMANDS_SCHEMA_UTILS_H_
diff --git a/buffet/commands/schema_utils_unittest.cc b/buffet/commands/schema_utils_unittest.cc
new file mode 100644
index 0000000..6918186
--- /dev/null
+++ b/buffet/commands/schema_utils_unittest.cc
@@ -0,0 +1,210 @@
+// Copyright 2014 The Chromium OS 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 <memory>
+#include <string>
+#include <vector>
+
+#include <base/json/json_reader.h>
+#include <base/json/json_writer.h>
+#include <base/values.h>
+#include <gtest/gtest.h>
+
+#include "buffet/commands/object_schema.h"
+#include "buffet/commands/prop_types.h"
+#include "buffet/commands/prop_values.h"
+#include "buffet/commands/schema_utils.h"
+
+namespace {
+// Helper method to create base::Value from a string as a smart pointer.
+// For ease of definition in C++ code, double-quotes in the source definition
+// are replaced with apostrophes.
+std::unique_ptr<base::Value> CreateValue(const char* json) {
+  std::string json2(json);
+  // Convert apostrophes to double-quotes so JSONReader can parse the string.
+  std::replace(json2.begin(), json2.end(), '\'', '"');
+  return std::unique_ptr<base::Value>(base::JSONReader::Read(json2));
+}
+
+// Helper method to create a JSON dictionary object from a string.
+std::unique_ptr<base::DictionaryValue> CreateDictionaryValue(const char* json) {
+  std::string json2(json);
+  std::replace(json2.begin(), json2.end(), '\'', '"');
+  base::Value* value = base::JSONReader::Read(json2);
+  base::DictionaryValue* dict;
+  value->GetAsDictionary(&dict);
+  return std::unique_ptr<base::DictionaryValue>(dict);
+}
+
+// Converts a JSON value to a string. It also converts double-quotes to
+// apostrophes for easy comparisons in C++ source code.
+std::string ValueToString(const base::Value* value) {
+  std::string json;
+  base::JSONWriter::Write(value, &json);
+  std::replace(json.begin(), json.end(), '"', '\'');
+  return json;
+}
+
+}  // namespace
+
+TEST(CommandSchemaUtils, TypedValueToJson_Scalar) {
+  EXPECT_EQ("true",
+            ValueToString(buffet::TypedValueToJson(true, nullptr).get()));
+  EXPECT_EQ("false",
+            ValueToString(buffet::TypedValueToJson(false, nullptr).get()));
+
+  EXPECT_EQ("0", ValueToString(buffet::TypedValueToJson(0, nullptr).get()));
+  EXPECT_EQ("-10", ValueToString(buffet::TypedValueToJson(-10, nullptr).get()));
+  EXPECT_EQ("20", ValueToString(buffet::TypedValueToJson(20, nullptr).get()));
+
+  EXPECT_EQ("0.0", ValueToString(buffet::TypedValueToJson(0.0, nullptr).get()));
+  EXPECT_EQ("1.2", ValueToString(buffet::TypedValueToJson(1.2, nullptr).get()));
+
+  EXPECT_EQ("'abc'",
+            ValueToString(buffet::TypedValueToJson(std::string("abc"),
+                                                   nullptr).get()));
+
+  std::vector<bool> bool_array{true, false};
+  EXPECT_EQ("[true,false]",
+            ValueToString(buffet::TypedValueToJson(bool_array, nullptr).get()));
+
+  std::vector<int> int_array{1, 2, 5};
+  EXPECT_EQ("[1,2,5]",
+            ValueToString(buffet::TypedValueToJson(int_array, nullptr).get()));
+
+  std::vector<double> dbl_array{1.1, 2.2};
+  EXPECT_EQ("[1.1,2.2]",
+            ValueToString(buffet::TypedValueToJson(dbl_array, nullptr).get()));
+
+  std::vector<std::string> str_array{"a", "bc"};
+  EXPECT_EQ("['a','bc']",
+            ValueToString(buffet::TypedValueToJson(str_array, nullptr).get()));
+}
+
+TEST(CommandSchemaUtils, TypedValueToJson_Object) {
+  buffet::IntPropType int_type;
+  buffet::native_types::Object object;
+
+  object.insert(std::make_pair("width", int_type.CreateValue(640)));
+  object.insert(std::make_pair("height", int_type.CreateValue(480)));
+  EXPECT_EQ("{'height':480,'width':640}",
+            ValueToString(buffet::TypedValueToJson(object, nullptr).get()));
+}
+
+TEST(CommandSchemaUtils, TypedValueFromJson_Bool) {
+  bool value;
+
+  EXPECT_TRUE(buffet::TypedValueFromJson(CreateValue("true").get(), nullptr,
+                                         &value, nullptr));
+  EXPECT_TRUE(value);
+
+  EXPECT_TRUE(buffet::TypedValueFromJson(CreateValue("false").get(), nullptr,
+                                         &value, nullptr));
+  EXPECT_FALSE(value);
+
+  buffet::ErrorPtr error;
+  EXPECT_FALSE(buffet::TypedValueFromJson(CreateValue("0").get(), nullptr,
+                                          &value, &error));
+  EXPECT_EQ("type_mismatch", error->GetCode());
+  error.reset();
+}
+
+TEST(CommandSchemaUtils, TypedValueFromJson_Int) {
+  int value;
+
+  EXPECT_TRUE(buffet::TypedValueFromJson(CreateValue("0").get(), nullptr,
+                                         &value, nullptr));
+  EXPECT_EQ(0, value);
+
+  EXPECT_TRUE(buffet::TypedValueFromJson(CreateValue("23").get(), nullptr,
+                                         &value, nullptr));
+  EXPECT_EQ(23, value);
+
+  EXPECT_TRUE(buffet::TypedValueFromJson(CreateValue("-1234").get(), nullptr,
+                                         &value, nullptr));
+  EXPECT_EQ(-1234, value);
+
+  buffet::ErrorPtr error;
+  EXPECT_FALSE(buffet::TypedValueFromJson(CreateValue("'abc'").get(), nullptr,
+                                          &value, &error));
+  EXPECT_EQ("type_mismatch", error->GetCode());
+  error.reset();
+}
+
+TEST(CommandSchemaUtils, TypedValueFromJson_Double) {
+  double value;
+
+  EXPECT_TRUE(buffet::TypedValueFromJson(CreateValue("0").get(), nullptr,
+                                         &value, nullptr));
+  EXPECT_DOUBLE_EQ(0.0, value);
+  EXPECT_TRUE(buffet::TypedValueFromJson(CreateValue("0.0").get(), nullptr,
+                                         &value, nullptr));
+  EXPECT_DOUBLE_EQ(0.0, value);
+
+  EXPECT_TRUE(buffet::TypedValueFromJson(CreateValue("23").get(), nullptr,
+                                         &value, nullptr));
+  EXPECT_EQ(23.0, value);
+  EXPECT_TRUE(buffet::TypedValueFromJson(CreateValue("23.1").get(), nullptr,
+                                         &value, nullptr));
+  EXPECT_EQ(23.1, value);
+
+  EXPECT_TRUE(buffet::TypedValueFromJson(CreateValue("-1.23E+02").get(),
+                                         nullptr, &value, nullptr));
+  EXPECT_EQ(-123.0, value);
+
+  buffet::ErrorPtr error;
+  EXPECT_FALSE(buffet::TypedValueFromJson(CreateValue("'abc'").get(), nullptr,
+                                          &value, &error));
+  EXPECT_EQ("type_mismatch", error->GetCode());
+  error.reset();
+}
+
+TEST(CommandSchemaUtils, TypedValueFromJson_String) {
+  std::string value;
+
+  EXPECT_TRUE(buffet::TypedValueFromJson(CreateValue("''").get(), nullptr,
+                                         &value, nullptr));
+  EXPECT_EQ("", value);
+
+  EXPECT_TRUE(buffet::TypedValueFromJson(CreateValue("'23'").get(), nullptr,
+                                         &value, nullptr));
+  EXPECT_EQ("23", value);
+
+  EXPECT_TRUE(buffet::TypedValueFromJson(CreateValue("'abc'").get(), nullptr,
+                                         &value, nullptr));
+  EXPECT_EQ("abc", value);
+
+  buffet::ErrorPtr error;
+  EXPECT_FALSE(buffet::TypedValueFromJson(CreateValue("12").get(), nullptr,
+                                          &value, &error));
+  EXPECT_EQ("type_mismatch", error->GetCode());
+  error.reset();
+}
+
+TEST(CommandSchemaUtils, TypedValueFromJson_Object) {
+  buffet::native_types::Object value;
+  buffet::ObjectSchema schema;
+
+  auto age_prop = std::make_shared<buffet::IntPropType>();
+  age_prop->AddMinMaxConstraint(0, 150);
+  schema.AddProp("age", age_prop);
+
+  auto name_prop = std::make_shared<buffet::StringPropType>();
+  name_prop->AddLengthConstraint(1, 30);
+  schema.AddProp("name", name_prop);
+
+  EXPECT_TRUE(buffet::TypedValueFromJson(
+      CreateValue("{'age':20,'name':'Bob'}").get(), &schema, &value, nullptr));
+  buffet::native_types::Object value2;
+  value2.insert(std::make_pair("age", age_prop->CreateValue(20)));
+  value2.insert(std::make_pair("name",
+                               name_prop->CreateValue(std::string("Bob"))));
+  EXPECT_EQ(value2, value);
+
+  buffet::ErrorPtr error;
+  EXPECT_FALSE(buffet::TypedValueFromJson(CreateValue("'abc'").get(), nullptr,
+                                          &value, &error));
+  EXPECT_EQ("type_mismatch", error->GetCode());
+  error.reset();
+}