瀏覽代碼

Initial commit of JSON parser/object. Contains the ability to directly map the JSON string to member variables inline. This skips over deserializing the object out into a JSON tree itself.

Samuel Jaffe 9 年之前
當前提交
a5d2abe2b5

+ 187 - 0
json.cpp

@@ -0,0 +1,187 @@
+//
+//  json.cpp
+//  json
+//
+//  Created by Sam Jaffe on 1/30/16.
+//  Copyright © 2016 Sam Jaffe. All rights reserved.
+//
+
+#include "json.h"
+#include "json_parser.hpp"
+#include "json_binder.hpp"
+
+struct test_t {
+  int a, b;
+};
+
+struct test_2_t {
+  test_t t;
+  double d;
+};
+
+int main(int argc, char const** argv) {
+  auto bind1 = json::binder::object_binder<test_t>{}
+  ("a", &test_t::a)
+  ("b", &test_t::b);
+  
+  json::binder::tuple_binder<test_t>{}
+  (&test_t::a)
+  (&test_t::b);
+  
+  auto bind2 = json::binder::object_binder<test_2_t>{}
+  ("t", &test_2_t::t, bind1)("d", &test_2_t::d);
+
+  {
+    test_t out;
+    std::string data = "{\"a\":1,\"b\":2}";
+  
+    json::parser::parse(json::binder::bind(out, bind1), data);
+    std::cout << out.a << ',' << out.b << std::endl;
+  }
+  {
+    test_2_t out;
+    std::string data = "{\"t\":{\"a\":1,\"b\":2},\"d\":1.5}";
+  
+    json::parser::parse(json::binder::bind(out, bind2), data);
+    std::cout << '{' << out.t.a << ',' << out.t.b << "}," << out.d << std::endl;
+  }
+
+}
+
+const json::value json::value::null_value{};
+
+namespace {
+  void parse_object(json::value& json, char const*& data);
+  void parse_array(json::value& json, char const*& data);
+  void parse_one_token(json::value& json, char const*& data);
+  
+  void parse_one_token(json::value& json, char const*& data) {
+    const char ch = json::helper::get_next_element(data);
+    if (ch == '{') {
+      parse_object(json, ++data);
+    } else if (ch == '[') {
+      parse_array(json, ++data);
+    } else if (ch == '"') {
+      json::helper::parse_string(json, ++data);
+    } else if (isnumber(ch)) {
+      json::helper::parse_numeric(json, data);
+    } else if (!strncmp(data, "true", 4)) {
+      json = true;
+    } else if (!strncmp(data, "false", 5)) {
+      json = false;
+    } else {
+      throw json::malformed_json_exception(std::string("Expected the start of a JSON element, found character '") + ch + "' instead");
+    }
+  }
+  
+  void parse_object(json::value& json, char const*& data) {
+    std::string key;
+    while (*data && *data != '}') {
+      json::helper::parse_string(key, data);
+      if (json::helper::get_next_element(data) != ':') {
+        throw json::malformed_json_exception(std::string("Expected key:value pair delimited by ':', got '") + *data + "' instead");
+      }
+      parse_one_token(json[key], ++data);
+      json::helper::advance_to_boundary<'}'>(data);
+    }
+    if (*data) ++data;
+    else throw json::malformed_json_exception("Reached end of parse string without finding object end");
+  }
+  
+  void parse_array(json::value& json, char const*& data) {
+    size_t current_idx = 0;
+    while (*data && *data != ']') {
+      parse_one_token(json[current_idx++], data);
+      json::helper::advance_to_boundary<']'>(data);
+    }
+    if (*data) ++data;
+    else throw json::malformed_json_exception("Reached end of parse string without finding array end");
+  }
+}
+
+namespace json {
+  namespace parser {
+    void parse(json::value& json, char const* data) {
+      parse_one_token(json, data);
+      if (*data) throw json::malformed_json_exception("Expected a single json token in top-level parse");
+    }
+  }
+}
+
+void json::value::parse(char const* data)  { parser::parse(*this, data); }
+void json::value::parse(std::string const& str) { parser::parse(*this, str); }
+void json::value::parse(std::istream & in)  { parser::parse(*this, in); }
+
+json::value& json::value::operator[](const size_t idx) {
+  if (!is_array()) {
+    data.set<array_jt>();
+  }
+  array_jt & val = data.get<array_jt>();
+  if (val.size() <= idx) val.resize(idx+1);
+  return val[idx];
+}
+
+json::value const& json::value::operator[](const size_t idx) const {
+  if (!is_array()) return null_value;
+  array_jt const& val = data.get<array_jt>();
+  if (val.size() <= idx) return null_value;
+  else return val[idx];
+}
+
+json::value& json::value::operator[](std::string const& key) {
+  if (!is_object()) {
+    data.set<object_jt>();
+  }
+  return data.get<object_jt>()[key];
+}
+
+json::value const& json::value::operator[](std::string const& key) const {
+  if (!is_object()) return null_value;
+  object_jt const& val = data.get<object_jt>();
+  auto it = val.find(key);
+  if (it != val.end()) {
+    return it->second;
+  } else {
+    return null_value;
+  }
+}
+
+json::value::string_jt const& json::value::as_string() const {
+  return data.get<string_jt>();
+}
+
+json::value::double_jt json::value::as_double() const {
+  if (data.is<double_jt>()) {
+    return data.get<double_jt>();
+  } else if (data.is<int_jt>()) {
+    return static_cast<double_jt>(data.get<int_jt>());
+  } else if (data.is<bool_jt>()) {
+    return data.get<bool_jt>() ? 1.0 : 0.0;
+  } else {
+    return 0.0;
+  }
+}
+
+json::value::int_jt json::value::as_int() const {
+  if (data.is<double_jt>()) {
+    return static_cast<int_jt>(data.get<double_jt>());
+  } else if (data.is<int_jt>()) {
+    return data.get<int_jt>();
+  } else if (data.is<bool_jt>()) {
+    return data.get<bool_jt>() ? 1 : 0;
+  } else {
+    return 0;
+  }
+}
+
+json::value::bool_jt json::value::as_bool() const {
+  if (data.is<double_jt>()) {
+    return data.get<double_jt>() != 0;
+  } else if (data.is<int_jt>()) {
+    return data.get<int_jt>() != 0;
+  } else if (data.is<bool_jt>()) {
+    return data.get<bool_jt>();
+  } else {
+    return false;
+  }
+}

+ 150 - 0
json.h

@@ -0,0 +1,150 @@
+//
+//  json.hpp
+//  json
+//
+//  Created by Sam Jaffe on 1/30/16.
+//  Copyright © 2016 Sam Jaffe. All rights reserved.
+//
+
+#ifndef json_hpp
+#define json_hpp
+
+#include <map>
+#include <vector>
+#include <string>
+#include <utility>
+
+#include "../variant/variant.hpp"
+
+#define JSON_TYPE_LIST \
+  X(object) \
+  X(array) \
+  X(string) \
+  X(double) \
+  X(int) \
+  X(bool)
+
+namespace json {
+  class value;
+  namespace parser {
+    template <typename T> void parse(T& json, char const* data);
+    template <typename T> void parse(T& json, std::string const& str);
+    template <typename T> void parse(T& json, std::istream & in);
+  }
+
+  class malformed_json_exception : public std::domain_error {
+    using std::domain_error::domain_error;
+  };
+    
+  class value {
+  public:
+    using object_jt = std::map<std::string, value>;
+    using array_jt = std::vector<value>;
+    using string_jt = std::string;
+    using double_jt = double;
+    using int_jt = int32_t;
+    using bool_jt = bool;
+  private:
+    static const value null_value;
+    
+    using data_t = variant<object_jt, array_jt, string_jt, double_jt, int_jt, bool_jt>;
+    data_t data;
+  public:
+#define X(type) bool is_##type() const { return data.is<type##_jt>(); }
+    JSON_TYPE_LIST
+#undef X
+    bool is_null() const { return !data.valid(); }
+    
+    value() = default;
+    value(value const&) = default;
+    value(value &&) = default;
+    value& operator=(value const&) = default;
+    value& operator=(value &&) = default;
+#define X(type) value(type##_jt val) { data.set<type##_jt>(std::move(val)); }
+    JSON_TYPE_LIST
+#undef X
+#define X(type) value(type##_jt && val) { data.set<type##_jt>(std::forward<type##_jt>(val)); }
+    JSON_TYPE_LIST
+#undef X
+#define X(type) value& operator=(type##_jt val) { data.set<type##_jt>(std::move(val)); return *this; }
+    JSON_TYPE_LIST
+#undef X
+    
+    void parse(char const* data);
+    void parse(std::string const& str);
+    void parse(std::istream & in);
+    
+    void clear() { data = data_t(); }
+    
+    value& operator[](const size_t idx);
+    value const& operator[](const size_t idx) const;
+    value& operator[](std::string const& key);
+    value const& operator[](std::string const& key) const;
+    
+    string_jt const& as_string() const;
+    double_jt as_double() const;
+    int_jt as_int() const;
+    bool_jt as_bool() const;
+    
+    operator string_jt const&() const { return as_string(); }
+    operator double_jt() const { return as_double(); }
+    operator int_jt() const { return as_int(); }
+    operator bool_jt() const { return as_bool(); }
+    
+  };
+#undef JSON_TYPE_LIST
+}
+
+namespace json {
+  namespace helper {
+    const char get_next_element(char const*& data) {
+      while (isspace(*data)) ++data;
+      return *data;
+    }
+    
+    template <char const E>
+    void advance_to_boundary(char const*& data) {
+      switch (get_next_element(data)) {
+        case ',': // This is there 'more data remains' token for the compound value
+          ++data;
+          break;
+        case E: // This is the end-token for the compound value
+          break;
+        default: // Error, malformed JSON
+          throw json::malformed_json_exception(std::string("Expected to recieve container delimiter ',' or '") + E + "', got '" + *data + "' instead");
+          break;
+      }
+    }
+    
+    template <typename T>
+    void parse_string(T& json, char const*& data) {
+      char const* start = data;
+      while (*++data) {
+        if (*data == '"' && *(data-1) != '\\') {
+          json = std::string(start+1, data);
+          ++data;
+          return;
+        }
+      }
+      throw json::malformed_json_exception("Could not locate end of string");
+    }
+    
+    template <typename T>
+    void parse_numeric(T& json, char const*& data) {
+      char const* start = data;
+      while (*++data) {
+        if (*data == '.') {
+          while (isnumber(*++data));
+          json = atof(start);
+          break;
+        } else if (!isnumber(*data)) {
+          json = atoi(start);
+          break;
+        }
+      }
+    }
+
+  }
+}
+
+#endif /* json_hpp */

+ 328 - 0
json.xcodeproj/project.pbxproj

@@ -0,0 +1,328 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		CDB2F7431C5D48090067C2EC /* json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB2F7411C5D48090067C2EC /* json.cpp */; };
+		CDB2F7441C5D48090067C2EC /* json.h in Headers */ = {isa = PBXBuildFile; fileRef = CDB2F7421C5D48090067C2EC /* json.h */; };
+		CDF643391C6E9AA20016A475 /* json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB2F7411C5D48090067C2EC /* json.cpp */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		CDF643301C6E9A8B0016A475 /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = /usr/share/man/man1/;
+			dstSubfolderSpec = 0;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 1;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		CDB2F7331C5D47F70067C2EC /* libjson.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libjson.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+		CDB2F7411C5D48090067C2EC /* json.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; path = json.cpp; sourceTree = "<group>"; tabWidth = 2; };
+		CDB2F7421C5D48090067C2EC /* json.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = json.h; sourceTree = "<group>"; tabWidth = 2; };
+		CDB2F7451C5E9BEB0067C2EC /* json_parser.hpp */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.cpp.h; path = json_parser.hpp; sourceTree = "<group>"; tabWidth = 2; };
+		CDB2F7461C5EA2E80067C2EC /* json_binder.hpp */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.cpp.h; path = json_binder.hpp; sourceTree = "<group>"; tabWidth = 2; };
+		CDF643321C6E9A8B0016A475 /* json-test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "json-test"; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		CDB2F7301C5D47F70067C2EC /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		CDF6432F1C6E9A8B0016A475 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		CDB2F72A1C5D47F70067C2EC = {
+			isa = PBXGroup;
+			children = (
+				CDB2F7411C5D48090067C2EC /* json.cpp */,
+				CDB2F7421C5D48090067C2EC /* json.h */,
+				CDB2F7451C5E9BEB0067C2EC /* json_parser.hpp */,
+				CDB2F7461C5EA2E80067C2EC /* json_binder.hpp */,
+				CDB2F7341C5D47F70067C2EC /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		CDB2F7341C5D47F70067C2EC /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CDB2F7331C5D47F70067C2EC /* libjson.dylib */,
+				CDF643321C6E9A8B0016A475 /* json-test */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+		CDB2F7311C5D47F70067C2EC /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CDB2F7441C5D48090067C2EC /* json.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+		CDB2F7321C5D47F70067C2EC /* json */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CDB2F73E1C5D47F70067C2EC /* Build configuration list for PBXNativeTarget "json" */;
+			buildPhases = (
+				CDB2F72F1C5D47F70067C2EC /* Sources */,
+				CDB2F7301C5D47F70067C2EC /* Frameworks */,
+				CDB2F7311C5D47F70067C2EC /* Headers */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = json;
+			productName = json;
+			productReference = CDB2F7331C5D47F70067C2EC /* libjson.dylib */;
+			productType = "com.apple.product-type.library.dynamic";
+		};
+		CDF643311C6E9A8B0016A475 /* json-test */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CDF643381C6E9A8B0016A475 /* Build configuration list for PBXNativeTarget "json-test" */;
+			buildPhases = (
+				CDF6432E1C6E9A8B0016A475 /* Sources */,
+				CDF6432F1C6E9A8B0016A475 /* Frameworks */,
+				CDF643301C6E9A8B0016A475 /* CopyFiles */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "json-test";
+			productName = "json-test";
+			productReference = CDF643321C6E9A8B0016A475 /* json-test */;
+			productType = "com.apple.product-type.tool";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		CDB2F72B1C5D47F70067C2EC /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0720;
+				ORGANIZATIONNAME = "Sam Jaffe";
+				TargetAttributes = {
+					CDB2F7321C5D47F70067C2EC = {
+						CreatedOnToolsVersion = 7.2;
+					};
+					CDF643311C6E9A8B0016A475 = {
+						CreatedOnToolsVersion = 7.2.1;
+					};
+				};
+			};
+			buildConfigurationList = CDB2F72E1C5D47F70067C2EC /* Build configuration list for PBXProject "json" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = CDB2F72A1C5D47F70067C2EC;
+			productRefGroup = CDB2F7341C5D47F70067C2EC /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				CDB2F7321C5D47F70067C2EC /* json */,
+				CDF643311C6E9A8B0016A475 /* json-test */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+		CDB2F72F1C5D47F70067C2EC /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CDB2F7431C5D48090067C2EC /* json.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		CDF6432E1C6E9A8B0016A475 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CDF643391C6E9AA20016A475 /* json.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		CDB2F73C1C5D47F70067C2EC /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = macosx;
+			};
+			name = Debug;
+		};
+		CDB2F73D1C5D47F70067C2EC /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = macosx;
+			};
+			name = Release;
+		};
+		CDB2F73F1C5D47F70067C2EC /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				EXECUTABLE_PREFIX = lib;
+				GCC_ENABLE_CPP_EXCEPTIONS = YES;
+				GCC_ENABLE_CPP_RTTI = YES;
+				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		CDB2F7401C5D47F70067C2EC /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				EXECUTABLE_PREFIX = lib;
+				GCC_ENABLE_CPP_EXCEPTIONS = YES;
+				GCC_ENABLE_CPP_RTTI = YES;
+				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+		CDF643361C6E9A8B0016A475 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		CDF643371C6E9A8B0016A475 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		CDB2F72E1C5D47F70067C2EC /* Build configuration list for PBXProject "json" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CDB2F73C1C5D47F70067C2EC /* Debug */,
+				CDB2F73D1C5D47F70067C2EC /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		CDB2F73E1C5D47F70067C2EC /* Build configuration list for PBXNativeTarget "json" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CDB2F73F1C5D47F70067C2EC /* Debug */,
+				CDB2F7401C5D47F70067C2EC /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		CDF643381C6E9A8B0016A475 /* Build configuration list for PBXNativeTarget "json-test" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CDF643361C6E9A8B0016A475 /* Debug */,
+				CDF643371C6E9A8B0016A475 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = CDB2F72B1C5D47F70067C2EC /* Project object */;
+}

+ 91 - 0
json.xcodeproj/xcuserdata/samjaffe.xcuserdatad/xcschemes/json-test.xcscheme

@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0720"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "CDF643311C6E9A8B0016A475"
+               BuildableName = "json-test"
+               BlueprintName = "json-test"
+               ReferencedContainer = "container:json.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CDF643311C6E9A8B0016A475"
+            BuildableName = "json-test"
+            BlueprintName = "json-test"
+            ReferencedContainer = "container:json.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CDF643311C6E9A8B0016A475"
+            BuildableName = "json-test"
+            BlueprintName = "json-test"
+            ReferencedContainer = "container:json.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CDF643311C6E9A8B0016A475"
+            BuildableName = "json-test"
+            BlueprintName = "json-test"
+            ReferencedContainer = "container:json.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 80 - 0
json.xcodeproj/xcuserdata/samjaffe.xcuserdatad/xcschemes/json.xcscheme

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0720"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "CDB2F7321C5D47F70067C2EC"
+               BuildableName = "libjson.dylib"
+               BlueprintName = "json"
+               ReferencedContainer = "container:json.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CDB2F7321C5D47F70067C2EC"
+            BuildableName = "libjson.dylib"
+            BlueprintName = "json"
+            ReferencedContainer = "container:json.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CDB2F7321C5D47F70067C2EC"
+            BuildableName = "libjson.dylib"
+            BlueprintName = "json"
+            ReferencedContainer = "container:json.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 32 - 0
json.xcodeproj/xcuserdata/samjaffe.xcuserdatad/xcschemes/xcschememanagement.plist

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>SchemeUserState</key>
+	<dict>
+		<key>json-test.xcscheme</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>15</integer>
+		</dict>
+		<key>json.xcscheme</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>14</integer>
+		</dict>
+	</dict>
+	<key>SuppressBuildableAutocreation</key>
+	<dict>
+		<key>CDB2F7321C5D47F70067C2EC</key>
+		<dict>
+			<key>primary</key>
+			<true/>
+		</dict>
+		<key>CDF643311C6E9A8B0016A475</key>
+		<dict>
+			<key>primary</key>
+			<true/>
+		</dict>
+	</dict>
+</dict>
+</plist>

+ 306 - 0
json_binder.hpp

@@ -0,0 +1,306 @@
+//
+//  json_binder.h
+//  json
+//
+//  Created by Sam Jaffe on 1/31/16.
+//  Copyright © 2016 Sam Jaffe. All rights reserved.
+//
+
+#ifndef json_binder_h
+#define json_binder_h
+#pragma once
+
+namespace json {
+  namespace binder {
+    template <typename T>
+    class binder_impl {
+    public:
+      virtual binder_impl<T>* clone() const = 0;
+      virtual ~binder_impl() {}
+      virtual void parse(T&, char const*&) const = 0;
+    };
+    
+    template <typename T>
+    class binder {
+    public:
+      binder() : impl(nullptr), owned(false) {}
+      binder(binder const& other) : impl(other.impl->clone()), owned(true) {}
+      binder(binder_impl<T> const* p) : impl(p), owned(true) {}
+      binder(binder_impl<T> const& r) : impl(&r), owned(false) {}
+      ~binder() { if (impl && owned) delete impl; }
+      
+      void parse(T& object, char const*& data) const {
+        if (!impl) return;
+        impl->parse(object, data);
+      }
+    private:
+      binder_impl<T> const* impl;
+      bool owned;
+    };
+    
+    template <typename T, typename E>
+    class direct_binder : public binder_impl<T> {
+    public:
+      direct_binder(E T::*p, binder<E> const& i) : ptr(p), impl(i) {}
+      virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
+      
+      virtual void parse(T& val, char const*& data) const override {
+        impl.parse(val.*ptr, data);
+      }
+    private:
+      E T::*ptr;
+      binder<E> impl;
+    };
+    
+    template <typename T>
+    class direct_binder<T, bool> : public binder_impl<T> {
+    public:
+      direct_binder(bool T::*p) : ptr(p) {}
+      virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
+      
+      virtual void parse(T& val, char const*& data) override {
+        if (!strncmp(data, "true", 4)) {
+          val.*ptr = true;
+        } else if (!strncmp(data, "false", 5)) {
+          val.*ptr = false;
+        } else {
+          throw json::malformed_json_exception("Expected a boolean type here");
+        }
+      }
+      
+    private:
+      bool T::*ptr;
+    };
+    
+    template <typename T>
+    class direct_binder<T, int> : public binder_impl<T> {
+    public:
+      direct_binder(int T::*p) : ptr(p) {}
+      virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
+
+      virtual void parse(T& val, char const*& data) const override {
+//        if (false) {
+        json::helper::parse_numeric(val.*ptr, data);
+//        } else {
+//          throw json::malformed_json_exception("Expected an integral type here");
+//        }
+      }
+    private:
+      int T::*ptr;
+    };
+    
+    template <typename T>
+    class direct_binder<T, double> : public binder_impl<T> {
+    public:
+      direct_binder(double T::*p) : ptr(p) {}
+      virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
+
+      virtual void parse(T& val, char const*& data) const override {
+//        if (false) {
+          json::helper::parse_numeric(val.*ptr, data);
+//        } else {
+//          throw json::malformed_json_exception("Expected a floating point type here");
+//        }
+      }
+    private:
+      double T::*ptr;
+    };
+    
+    template <typename T>
+    class direct_binder<T, std::string> : public binder_impl<T> {
+    public:
+      direct_binder(std::string T::*p) : ptr(p) {}
+      virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
+
+      virtual void parse(T& val, char const*& data) const override {
+        json::helper::parse_string(val.*ptr, data);
+      }
+    private:
+      std::string T::*ptr;
+    };
+    
+    template <typename T, typename V>
+    class direct_binder<T, std::vector<V> > : public binder_impl<T> {
+    public:
+      direct_binder(std::vector<V> T::*p, binder<V> const&i);
+      virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
+      
+      virtual void parse(T& val, char const*& data) const override {
+        const char ch = json::helper::get_next_element(data);
+        if (ch != '[') {
+          throw json::malformed_json_exception("Expected an array type");
+        }
+        ++data;
+
+        V to_make;
+        std::vector<V>& vec = val.*ptr;
+        while (*data && *data != ']') {
+          impl.parse(to_make, data);
+          vec.emplace_back(to_make);
+          json::helper::advance_to_boundary<']'>(data);
+        }
+        if (*data) ++data;
+        else throw json::malformed_json_exception("Reached end of parse string without finding array end");
+      }
+    private:
+      std::vector<V> T::*ptr;
+      binder<V> impl;
+    };
+    
+    template <typename T, typename V>
+    class direct_binder<T, std::map<std::string, V> > : public binder_impl<T> {
+    public:
+      direct_binder(std::vector<V> T::*p, binder<V> const&i);
+      virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
+      
+      virtual void parse(T& val, char const*& data) const override {
+        const char ch = json::helper::get_next_element(data);
+        if (ch != '{') {
+          throw json::malformed_json_exception("Expected an array type");
+        }
+        ++data;
+        
+        V to_make;
+        std::map<std::string, V>& vec = val.*ptr;
+        std::string key;
+        while (*data && *data != '}') {
+          json::helper::parse_string(key, data);
+          if (json::helper::get_next_element(data) != ':') {
+            throw json::malformed_json_exception(std::string("Expected key:value pair delimited by ':', got '") + *data + "' instead");
+          }
+          impl.parse(to_make, ++data);
+          vec.emplace(key, to_make);
+          json::helper::advance_to_boundary<'}'>(data);
+        }
+        if (*data) ++data;
+        else throw json::malformed_json_exception("Reached end of parse string without finding object end");
+      }
+    private:
+      std::map<std::string, V> T::*ptr;
+      binder<V> impl;
+    };
+    
+    template <typename T>
+    class object_binder : public binder_impl<T> {
+    public:
+      object_binder() {}
+      virtual binder_impl<T>* clone() const override { return new object_binder(*this); }
+
+      template <typename V>
+      object_binder& operator()(std::string const&k, V T::*ptr, binder_impl<V> const&v) {
+        return (*this)(k, binder<T>(new direct_binder<T, V>(ptr, binder<V>(v) )));
+      }
+      
+      object_binder& operator()(std::string const&k, binder<T> const&v) {
+        mapping.emplace(k, v);
+        return *this;
+      }
+      
+      virtual void parse(T& object, char const*& data) const override {
+        const char ch = json::helper::get_next_element(data);
+        if (ch == '{') {
+          parse_object(object, ++data);
+        } else {
+          throw json::malformed_json_exception(std::string("Expected an object type for binding to ") + typeid(T).name());
+        }
+      }
+      
+      void parse_object(T& object, char const*& data) const {
+        std::string key;
+        while (*data && *data != '}') {
+          json::helper::parse_string(key, data);
+          if (json::helper::get_next_element(data) != ':') {
+            throw json::malformed_json_exception(std::string("Expected key:value pair delimited by ':', got '") + *data + "' instead");
+          }
+          auto it = mapping.find(key);
+          if (it != mapping.end()) {
+            it->second.parse(object, ++data);
+          } else {
+            throw json::malformed_json_exception("Unexpected key " + key);
+          }
+          json::helper::advance_to_boundary<'}'>(data);
+        }
+        if (*data) ++data;
+        else throw json::malformed_json_exception("Reached end of parse string without finding object end");
+      }
+
+      template <typename E>
+      object_binder& operator()(std::string const& s, E T::*p) {
+        return operator()(s, binder<T>(new direct_binder<T, E>(p)));
+      }
+    private:
+      std::map<std::string, binder<T>> mapping;
+    };
+    
+    template <typename T>
+    class tuple_binder : public binder_impl<T> {
+    public:
+      virtual binder_impl<T>* clone() const override { return new tuple_binder(*this); }
+
+      tuple_binder& operator()(binder<T> const&b) {
+        members.push_back(b);
+        return *this;
+      }
+      
+      virtual void parse(T& object, char const*& data) const override {
+        const char ch = json::helper::get_next_element(data);
+        if (ch == '[') {
+          parse_tuple(object, ++data);
+        } else {
+          throw json::malformed_json_exception(std::string("Expected an object type for binding to ") + typeid(T).name());
+        }
+      }
+      
+      void parse_tuple(T& object, char const*& data) const {
+        auto it = members.begin();
+        while (*data && *data != ']' && it != members.end()) {
+          it->parse(object, data);
+          json::helper::advance_to_boundary<']'>(data);
+          ++it;
+        }
+        if (it != members.end()) {
+          throw json::malformed_json_exception("Failed to parse every member of tuple");
+        }
+        if (*data) ++data;
+        else throw json::malformed_json_exception("Reached end of parse string without finding array end");
+      }
+
+      template <typename E>
+      tuple_binder& operator()(E T::*p) {
+        return operator()(binder<T>(new direct_binder<T, E>(p)));
+      }
+    
+    private:
+      std::vector<binder<T>> members;
+    };
+    
+    template <typename T>
+    class visitor {
+    public:
+      visitor(T& o, binder_impl<T>& b) : obj(o), b(b) {}
+      
+      void parse(char const* data) {
+        b.parse(obj, data);
+      }
+      
+    private:
+      T& obj;
+      binder_impl<T>& b;
+    };
+    
+    template <typename T>
+    visitor<T> bind(T& object, binder_impl<T>& b) {
+      return visitor<T>{object, b};
+    }
+  }
+  
+  namespace parser {
+    template <typename T>
+    void parse(binder::visitor<T>& visitor, char const* data) {
+      visitor.parse(data);
+    }    
+  }
+  
+}
+
+#endif /* json_binder_h */

+ 49 - 0
json_parser.hpp

@@ -0,0 +1,49 @@
+//
+//  json_binder.h
+//  json
+//
+//  Created by Sam Jaffe on 1/31/16.
+//  Copyright © 2016 Sam Jaffe. All rights reserved.
+//
+
+#ifndef json_parser_h
+#define json_parser_h
+#pragma once
+
+#include "json.h"
+
+#include <iostream>
+
+namespace json {
+  
+  namespace binder { template <typename> class visitor; }
+  
+  namespace parser {
+    void parse(value& json, char const* data);
+    
+    template <typename T>
+    void parse(binder::visitor<T>&, char const*);
+    
+    template <typename T>
+    void parse(T& json, std::string const& str) {
+      parse(json, str.c_str());
+    }
+    
+    template <typename T>
+    void parse(T& json, std::istream & in) {
+      in.seekg(0, std::ios_base::end);
+      size_t end = in.tellg();
+      char data[end];
+      in.seekg(0);
+      in.read(data, end);
+      parse(json, data);
+    }
+    
+    template <typename T, typename S>
+    void parse(binder::visitor<T>&& v, S& s) {
+      parse(v, s);
+    }
+  }
+}
+
+#endif /* json_parser_h */