Subsonic API: switch from property_tree to Wt::Json to properly report empty arrays/nodes

This commit is contained in:
emeric
2019-04-07 19:25:08 +02:00
parent 99b56da15f
commit 93cb5cd50c
2 changed files with 67 additions and 40 deletions
+64 -39
View File
@@ -19,7 +19,10 @@
#include "SubsonicResponse.hpp" #include "SubsonicResponse.hpp"
#include <regex> #include <Wt/Json/Array.h>
#include <Wt/Json/Object.h>
#include <Wt/Json/Value.h>
#include <Wt/Json/Serializer.h>
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/xml_parser.hpp> #include <boost/property_tree/xml_parser.hpp>
@@ -192,74 +195,96 @@ Response::createArrayNode(const std::string& key)
void void
Response::write(std::ostream& os, ResponseFormat format) Response::write(std::ostream& os, ResponseFormat format)
{ {
std::function<boost::property_tree::ptree(const Response::Node&, ResponseFormat)> nodeToPropertyTree = [&] (const Response::Node& node, ResponseFormat format) switch (format)
{
case ResponseFormat::xml:
writeXML(os);
break;
case ResponseFormat::json:
writeJSON(os);
break;
}
}
void
Response::writeXML(std::ostream& os)
{
std::function<boost::property_tree::ptree(const Response::Node&)> nodeToPropertyTree = [&] (const Response::Node& node)
{ {
boost::property_tree::ptree res; boost::property_tree::ptree res;
for (auto itAttribute : node._attributes) for (auto itAttribute : node._attributes)
{ res.put("<xmlattr>." + itAttribute.first, itAttribute.second);
std::string key {format == ResponseFormat::xml ? "<xmlattr>." : ""};
key += itAttribute.first;
res.put(key, itAttribute.second);
}
if (!node._value.empty()) if (!node._value.empty())
{ {
if (format == ResponseFormat::json) res.put_value(node._value);
res.put("value", node._value);
else
res.put_value(node._value);
} }
else else
{ {
for (auto itChildNode : node._children) for (auto itChildNode : node._children)
{ {
for (const Response::Node& childNode : itChildNode.second) for (const Response::Node& childNode : itChildNode.second)
res.add_child(itChildNode.first, nodeToPropertyTree(childNode, format)); res.add_child(itChildNode.first, nodeToPropertyTree(childNode));
} }
for (auto itChildArrayNode : node._childrenArrays) for (auto itChildArrayNode : node._childrenArrays)
{ {
const std::vector<Response::Node>& childArrayNodes {itChildArrayNode .second}; const std::vector<Response::Node>& childArrayNodes {itChildArrayNode .second};
if (format == ResponseFormat::json) for (const Response::Node& childNode : childArrayNodes )
{ res.add_child(itChildArrayNode.first, nodeToPropertyTree(childNode));
boost::property_tree::ptree array;
for (const Response::Node& childNode : childArrayNodes )
array.push_back(std::make_pair("", nodeToPropertyTree(childNode, format)));
res.add_child(itChildArrayNode.first, array);
}
else
{
for (const Response::Node& childNode : childArrayNodes )
res.add_child(itChildArrayNode.first, nodeToPropertyTree(childNode, format));
}
} }
} }
return res; return res;
}; };
boost::property_tree::ptree root {nodeToPropertyTree(_root)};
boost::property_tree::write_xml(os, root);
}
boost::property_tree::ptree root {nodeToPropertyTree(_root, format)}; void
Response::writeJSON(std::ostream& os)
{
namespace Json = Wt::Json;
switch (format) std::function<Json::Object(const Response::Node&)> nodeToJsonObject = [&] (const Response::Node& node)
{ {
case ResponseFormat::xml: Json::Object res;
boost::property_tree::write_xml(os, root);
break; for (auto itAttribute : node._attributes)
case ResponseFormat::json: res[itAttribute.first] = Json::Value {itAttribute.second};
if (!node._value.empty())
{ {
// property_tree does not support empty json array res["value"] = Json::Value {node._value};
std::ostringstream oss;
boost::property_tree::write_json(oss, root);
os << std::regex_replace(oss.str(), std::regex {R"(\[[\r\n]*\s*\"\"[\r\n]*\s*\])"}, R"({})");
break;
} }
} else
{
for (auto itChildNode : node._children)
{
for (const Response::Node& childNode : itChildNode.second)
res[itChildNode.first] = nodeToJsonObject(childNode);
}
for (auto itChildArrayNode : node._childrenArrays)
{
const std::vector<Response::Node>& childArrayNodes {itChildArrayNode .second};
Json::Array array;
for (const Response::Node& childNode : childArrayNodes )
array.emplace_back(nodeToJsonObject(childNode));
res[itChildArrayNode.first] = std::move(array);
}
}
return res;
};
Json::Object root {nodeToJsonObject(_root)};
os << Json::serialize(root);
} }
} // namespace } // namespace
+3 -1
View File
@@ -101,10 +101,12 @@ class Response
Node& createNode(const std::string& key); Node& createNode(const std::string& key);
Node& createArrayNode(const std::string& key); Node& createArrayNode(const std::string& key);
void writeJson(std::ostream& os);
void write(std::ostream& os, ResponseFormat format); void write(std::ostream& os, ResponseFormat format);
private: private:
void writeJSON(std::ostream& os);
void writeXML(std::ostream& os);
Response() = default; Response() = default;
Node _root; Node _root;
}; };