Add first work package for REST API implementation
[anna.git] / include / anna / json / SaxConsumer.hpp
index a3cd194..1557575 100644 (file)
@@ -21,106 +21,121 @@ namespace json {
 
 class SaxConsumer : public nlohmann::json::json_sax_t
 {
+  char attr_prefix_;
   int indent_;
-  bool started_, last_was_start_, last_was_array_;
   std::stringstream result_;
   std::stringstream current_object_;
   std::stack<std::string> nodes_stack_;
   std::string key_;
+  bool has_attributes_;
+
+  const std::string & get_top() { return nodes_stack_.top(); }
+
 
   public:
 
-  SaxConsumer() : started_(false), last_was_start_(false), last_was_array_(false), indent_(-ANNA_XML_INDENTATION_SPACES) {}
+  SaxConsumer(char attrPrefix = '@') : attr_prefix_(attrPrefix),
+                                           has_attributes_(false),
+                                           indent_(-ANNA_XML_INDENTATION_SPACES) {};
 
   const std::stringstream & getResult() const { return result_; }
 
   bool null() override
   {
     current_object_ << "<null>";
-    last_was_start_ = false;
+    has_attributes_ = true;
     return true;
   }
 
   bool boolean(bool val) override
   {
     current_object_ << std::quoted(val ? "true" : "false");
-    last_was_start_ = false;
+    has_attributes_ = true;
     return true;
   }
 
   bool number_integer(number_integer_t val) override
   {
     current_object_ << std::quoted(std::to_string(val));
-    last_was_start_ = false;
+    has_attributes_ = true;
     return true;
   }
 
   bool number_unsigned(number_unsigned_t val) override
   {
     current_object_ << std::quoted(std::to_string(val));
-    last_was_start_ = false;
+    has_attributes_ = true;
     return true;
   }
 
   bool number_float(number_float_t val, const string_t& s) override
   {
     current_object_ << std::quoted(s);
-    last_was_start_ = false;
+    has_attributes_ = true;
     return true;
   }
 
   bool string(string_t& val) override
   {
     current_object_ << std::quoted(val);
-    last_was_start_ = false;
+    has_attributes_ = true;
     return true;
   }
 
   bool start_object(std::size_t elements) override
   {
-    if (!started_) { started_ = true ; return true; }
-    indent_ += ANNA_XML_INDENTATION_SPACES;
-    if (last_was_start_) result_ << ">\n";
-    last_was_start_ = true;
-    if (!last_was_array_) nodes_stack_.push(key_);
-    result_ << std::string(indent_, ' ') << "<" << nodes_stack_.top();
-    if (last_was_array_) nodes_stack_.push(key_);
-    last_was_array_ = false;
+    if (key_ == "") return true; // global object condition (first start object)
+    nodes_stack_.push(key_); // push on starts
+    indent_ += ANNA_XML_INDENTATION_SPACES; // increase indentation on object start
+
+    if (indent_ != 0) {
+      // New object when previous hadn't attributes:
+      if (current_object_.str().empty() && !has_attributes_) result_ <<">";
+      result_ << "\n";
+    }
+    result_ << std::string(indent_, ' ') << "<" << get_top();
+
+    has_attributes_ = false;
     return true;
   }
 
   bool end_object() override
   {
-    std::string close = "/>";
-    if (current_object_.str().empty()) close = "";
-    result_ << current_object_.str() << close;
     if (indent_ < 0) return true;
-    if (close == "") result_ << std::string(indent_, ' ') << "</" << nodes_stack_.top() << ">";
-    indent_ -= ANNA_XML_INDENTATION_SPACES;
-    result_ << "\n";
-    current_object_.str("");
-    nodes_stack_.pop();
+    if (current_object_.str().empty()) {
+      result_ << "\n" << std::string(indent_, ' ') << "</" << get_top() << ">";
+    }
+    else {
+      result_ << current_object_.str() << "/>";
+      current_object_.str("");
+    }
+
+    nodes_stack_.pop(); // pop on ends
+    indent_ -= ANNA_XML_INDENTATION_SPACES; // decrease indentation on object end
     return true;
   }
 
   bool start_array(std::size_t elements) override
   {
-    nodes_stack_.push(key_);
-    result_ << current_object_.str() << ">\n";
+    nodes_stack_.push(key_); // push on starts
+
+    result_ << current_object_.str();
+
     current_object_.str("");
+    has_attributes_ = false;
+
     return true;
   }
 
   bool end_array() override
   {
-    nodes_stack_.pop();
-    last_was_array_ = true;
+    nodes_stack_.pop(); // pop on ends
     return true;
   }
 
   bool key(string_t& val) override
   {
-    if (val[0] != '@') {
+    if (val[0] != attr_prefix_) {
       key_ = val;
     }
     else {