فهرست منبع

Moved example program to json_test.cpp
Moved common functions to json_common.h, disassociating json::value and json::binder, which are ultimately separate constructs currently.

Samuel Jaffe 9 سال پیش
والد
کامیت
7295113df9
7فایلهای تغییر یافته به همراه232 افزوده شده و 110 حذف شده
  1. 0 38
      json.cpp
  2. 2 58
      json.h
  3. 7 2
      json.xcodeproj/project.pbxproj
  4. 94 5
      json_binder.hpp
  5. 71 0
      json_common.h
  6. 6 7
      json_parser.hpp
  7. 52 0
      json_test.cpp

+ 0 - 38
json.cpp

@@ -10,44 +10,6 @@
 #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 {

+ 2 - 58
json.h

@@ -11,10 +11,10 @@
 
 #include <map>
 #include <vector>
-#include <string>
 #include <utility>
 
 #include "../variant/variant.hpp"
+#include "json_common.h"
 
 #define JSON_TYPE_LIST \
   X(object) \
@@ -31,11 +31,7 @@ namespace json {
     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>;
@@ -95,56 +91,4 @@ namespace json {
 #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 */

+ 7 - 2
json.xcodeproj/project.pbxproj

@@ -7,9 +7,9 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		CD217D911CCAD587007C50C6 /* json_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD217D8F1CCAD587007C50C6 /* json_test.cpp */; };
 		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 */
@@ -25,6 +25,8 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+		CD217D8F1CCAD587007C50C6 /* json_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_test.cpp; sourceTree = "<group>"; };
+		CD217D921CCAD885007C50C6 /* json_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = json_common.h; sourceTree = "<group>"; };
 		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; };
@@ -54,8 +56,10 @@
 		CDB2F72A1C5D47F70067C2EC = {
 			isa = PBXGroup;
 			children = (
+				CD217D8F1CCAD587007C50C6 /* json_test.cpp */,
 				CDB2F7411C5D48090067C2EC /* json.cpp */,
 				CDB2F7421C5D48090067C2EC /* json.h */,
+				CD217D921CCAD885007C50C6 /* json_common.h */,
 				CDB2F7451C5E9BEB0067C2EC /* json_parser.hpp */,
 				CDB2F7461C5EA2E80067C2EC /* json_binder.hpp */,
 				CDB2F7341C5D47F70067C2EC /* Products */,
@@ -167,7 +171,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CDF643391C6E9AA20016A475 /* json.cpp in Sources */,
+				CD217D911CCAD587007C50C6 /* json_test.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -321,6 +325,7 @@
 				CDF643371C6E9A8B0016A475 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
 		};
 /* End XCConfigurationList section */
 	};

+ 94 - 5
json_binder.hpp

@@ -10,6 +10,12 @@
 #define json_binder_h
 #pragma once
 
+#include "json_common.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
 namespace json {
   namespace binder {
     template <typename T>
@@ -18,6 +24,7 @@ namespace json {
       virtual binder_impl<T>* clone() const = 0;
       virtual ~binder_impl() {}
       virtual void parse(T&, char const*&) const = 0;
+      virtual void write(T const&, std::string &) const = 0;
     };
     
     template <typename T>
@@ -33,6 +40,11 @@ namespace json {
         if (!impl) return;
         impl->parse(object, data);
       }
+      
+      void write(T const& object, std::string & data) const {
+        if (!impl) return;
+        impl->write(object, data);
+      }
     private:
       binder_impl<T> const* impl;
       bool owned;
@@ -47,6 +59,10 @@ namespace json {
       virtual void parse(T& val, char const*& data) const override {
         impl.parse(val.*ptr, data);
       }
+      
+      virtual void write(T const& val, std::string & data) const override {
+        impl.write(val.*ptr, data);
+      }
     private:
       E T::*ptr;
       binder<E> impl;
@@ -58,7 +74,7 @@ namespace json {
       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 {
+      virtual void parse(T& val, char const*& data) const override {
         if (!strncmp(data, "true", 4)) {
           val.*ptr = true;
         } else if (!strncmp(data, "false", 5)) {
@@ -68,6 +84,10 @@ namespace json {
         }
       }
       
+      virtual void write(T const& val, std::string & data) const override {
+        data += (val.*ptr ? "true" : "false");
+      }
+      
     private:
       bool T::*ptr;
     };
@@ -85,6 +105,12 @@ namespace json {
 //          throw json::malformed_json_exception("Expected an integral type here");
 //        }
       }
+      
+      virtual void write(T const& val, std::string & data) const override {
+        char buffer[16] = { '\0' };
+        snprintf(buffer, sizeof(buffer), "%d", val.*ptr);
+        data += buffer;
+      }
     private:
       int T::*ptr;
     };
@@ -102,6 +128,12 @@ namespace json {
 //          throw json::malformed_json_exception("Expected a floating point type here");
 //        }
       }
+      
+      virtual void write(T const& val, std::string & data) const override {
+        char buffer[32] = { '\0' };
+        snprintf(buffer, sizeof(buffer), "%lf", val.*ptr);
+        data += buffer;
+      }
     private:
       double T::*ptr;
     };
@@ -115,6 +147,10 @@ namespace json {
       virtual void parse(T& val, char const*& data) const override {
         json::helper::parse_string(val.*ptr, data);
       }
+      
+      virtual void write(T const& val, std::string & data) const override {
+        data += "\"" + val.*ptr + "\"";
+      }
     private:
       std::string T::*ptr;
     };
@@ -142,6 +178,17 @@ namespace json {
         if (*data) ++data;
         else throw json::malformed_json_exception("Reached end of parse string without finding array end");
       }
+      
+      virtual void write(T const& val, std::string & data) const override {
+        data += "[";
+        std::vector<V> const & vec = val.*ptr;
+        for (typename std::vector<V>::const_iterator it = vec.begin(),
+             end = vec.end(); it != end; ++it) {
+          impl.write(*it, data);
+          data += ",";
+        }
+        data.back() = ']';
+      }
     private:
       std::vector<V> T::*ptr;
       binder<V> impl;
@@ -175,6 +222,18 @@ namespace json {
         if (*data) ++data;
         else throw json::malformed_json_exception("Reached end of parse string without finding object end");
       }
+      
+      virtual void write(T const& val, std::string & data) const override {
+        data += "{";
+        std::map<std::string, V> const & map = val.*ptr;
+        for (typename std::map<std::string, V>::const_iterator it = map.begin(),
+             end = map.end(); it != end; ++it) {
+          data += "\"" + it->first + "\":";
+          impl.write(it->second, data);
+          data += ",";
+        }
+        data.back() = '}';
+      }
     private:
       std::map<std::string, V> T::*ptr;
       binder<V> impl;
@@ -205,6 +264,17 @@ namespace json {
         }
       }
       
+      virtual void write(T const& val, std::string & data) const override {
+        data += "{";
+        for (typename std::map<std::string, binder<T>>::const_iterator it = mapping.begin(),
+             end = mapping.end(); it != end; ++it) {
+          data += "\"" + it->first + "\":";
+          it->second.write(val, data);
+          data += ",";
+        }
+        data.back() = '}';
+      }
+      
       void parse_object(T& object, char const*& data) const {
         std::string key;
         while (*data && *data != '}') {
@@ -251,6 +321,16 @@ namespace json {
         }
       }
       
+      virtual void write(T const& val, std::string & data) const override {
+        data += "[";
+        for (typename std::vector<binder<T>>::const_iterator it = members.begin(),
+             end = members.end(); it != end; ++it) {
+          it->write(val, data);
+          data += ",";
+        }
+        data.back() = ']';
+      }
+      
       void parse_tuple(T& object, char const*& data) const {
         auto it = members.begin();
         while (*data && *data != ']' && it != members.end()) {
@@ -274,17 +354,21 @@ namespace json {
       std::vector<binder<T>> members;
     };
     
-    template <typename T>
+    template <typename T, typename S = T>
     class visitor {
     public:
-      visitor(T& o, binder_impl<T>& b) : obj(o), b(b) {}
+      visitor(S& o, binder_impl<T>& b) : obj(o), b(b) {}
       
       void parse(char const* data) {
         b.parse(obj, data);
       }
       
+      void write(std::string & data) const {
+        b.write(obj, data);
+      }
+      
     private:
-      T& obj;
+      S& obj;
       binder_impl<T>& b;
     };
     
@@ -298,7 +382,12 @@ namespace json {
     template <typename T>
     void parse(binder::visitor<T>& visitor, char const* data) {
       visitor.parse(data);
-    }    
+    }
+    
+    template <typename T, typename S>
+    void write(binder::visitor<T, S> const & visitor, std::string & data) {
+      visitor.write(data);
+    }
   }
   
 }

+ 71 - 0
json_common.h

@@ -0,0 +1,71 @@
+//
+//  json_common.h
+//  json
+//
+//  Created by Sam Jaffe on 4/22/16.
+//
+
+#pragma once
+
+#include <cstdlib>
+#include <stdexcept>
+#include <string>
+
+namespace json {
+  class malformed_json_exception : public std::domain_error {
+    using std::domain_error::domain_error;
+  };
+}
+
+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) {
+      // TODO: more sophisticated float detection?
+      char const* start = data;
+      while (*++data) {
+        if (*data == '.') {
+          while (isnumber(*++data));
+          json = atof(start);
+          break;
+        } else if (!isnumber(*data)) {
+          json = atoi(start);
+          break;
+        }
+      }
+    }
+    
+  }
+}

+ 6 - 7
json_parser.hpp

@@ -10,19 +10,18 @@
 #define json_parser_h
 #pragma once
 
-#include "json.h"
-
 #include <iostream>
 
 namespace json {
+  class value;
   
-  namespace binder { template <typename> class visitor; }
+  namespace binder { template <typename, 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, typename S>
+    void parse(binder::visitor<T, S>&, char const*);
     
     template <typename T>
     void parse(T& json, std::string const& str) {
@@ -39,8 +38,8 @@ namespace json {
       parse(json, data);
     }
     
-    template <typename T, typename S>
-    void parse(binder::visitor<T>&& v, S& s) {
+    template <typename T, typename S, typename V>
+    void parse(binder::visitor<T, S>&& v, V& s) {
       parse(v, s);
     }
   }

+ 52 - 0
json_test.cpp

@@ -0,0 +1,52 @@
+//
+//  json_test.cpp
+//  json
+//
+//  Created by Sam Jaffe on 4/22/16.
+//
+
+#include "json_binder.hpp"
+#include "json_parser.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}";
+    std::cout << data << std::endl;
+
+    json::parser::parse(json::binder::bind(out, bind2), data);
+    std::cout << '{' << out.t.a << ',' << out.t.b << "}," << out.d << std::endl;
+    
+    std::string sout;
+    json::parser::write(json::binder::bind(out, bind2), sout);
+    std::cout << sout << std::endl;
+  }
+  
+}