drogonframework / drogon

Drogon: A C++14/17/20 based HTTP web application framework running on Linux/macOS/Unix/Windows
MIT License
11.06k stars 1.06k forks source link

WebSockets sending JSON data #1948

Closed Mis1eader-dev closed 3 months ago

Mis1eader-dev commented 4 months ago

Is your feature request related to a problem? Please describe. Currently websocket connections can only send const string& and const char*, for it to send const Json::Value& we have to stringify it first, and then send it over the const string& overload. The problem is, there is only one public method exposed by Json::Value to construct a stringified JSON, that is Json::Value::toStyledString. But it outputs a prettified version of the JSON, with indents. There is Json::StreamWriter, but that is a lot of manual work.

Describe the solution you'd like A public overloaded method for drogon::WebSocketConnection taking in a const Json::Value&, to stringify efficiently, just like how drogon::HttpResponse::newHttpJsonResponse(json) generates an efficient stringified version of the JSON.

hwc0919 commented 3 months ago

I write a format function for Json::Value myself. I think this is the one of the unfriendly places of jsoncpp. nlohmann json has a dump() method, which is really good.

I personally don't want to add any more Jsoncpp relative apis into framework. It really sucks, in both api design and performance. Perhaps we will get rid of it in future.

Mis1eader-dev commented 3 months ago

Perhaps we can write a middle layer API that reveives the const Json::Value& and spits out a dumped JSON string, this way we know where it has been referenced and can change its signature easily

hwc0919 commented 3 months ago

@Mis1eader-dev You are right.

This is my version of 'parse' and 'dump'.

The dump api is simple, but I don't know if it is a standard practice to use a static builder...

The parse api is still ugly... There are so many different ways to handle errors, and we don't have a standard for that.

I don't feel like forcing others to use my way, so I won't want to write this PR :see_no_evil:. I'll leave it to someone who wants.

#include <json/reader.h>
#include <json/writer.h>

std::string jsonToString(const Json::Value & json)
{
    static const Json::StreamWriterBuilder & builder = []() -> Json::StreamWriterBuilder & {
        static Json::StreamWriterBuilder builder;
        builder["commentStyle"] = "None";
        builder["indentation"] = "";
        return builder;
    }();
    return writeString(builder, json);
}

bool stringToJson(const std::string & doc, Json::Value * root, Json::String * errs)
{
    return stringToJson(&doc[0], &doc[0] + doc.size(), root, errs);
}

bool stringToJson(char const * beginDoc, char const * endDoc, Json::Value * root, Json::String * errs)
{
    static Json::CharReaderBuilder & builder = []() -> Json::CharReaderBuilder & {
        static Json::CharReaderBuilder builder;
        builder["collectComments"] = false;
        return builder;
    }();
    std::unique_ptr<Json::CharReader> jsonReader(builder.newCharReader());
    return jsonReader->parse(beginDoc, endDoc, root, errs);
}
Mis1eader-dev commented 3 months ago

Why no PR? It's better to have something than not have JSON for WebSockets We can copy the logic written in the HttpResponse implementation for JSON responses and make a drogon::json middle layer containing both strToJson and jsonToStr methods For strToJson we'll use the logic of HttpRequest JSON parsing