Skip to content

Commit 405df2f

Browse files
authored
Merge pull request #316 from wravery/next
feat(json): default taocpp-json impl for graphqljson
2 parents 8eb93c4 + 7cee86d commit 405df2f

File tree

6 files changed

+290
-20
lines changed

6 files changed

+290
-20
lines changed

.github/workflows/linux.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
run: cmake -E make_directory build
3636

3737
- name: Configure CMake
38-
run: cmake --preset ${{ matrix.compiler }}-${{ matrix.config }} -DCMAKE_TOOLCHAIN_FILE=
38+
run: cmake --preset ${{ matrix.compiler }}-${{ matrix.config }} -DGRAPHQL_USE_TAOCPP_JSON=OFF -DCMAKE_TOOLCHAIN_FILE=
3939

4040
- name: Build
4141
run: cmake --build --preset ${{ matrix.compiler }}-${{ matrix.config }} -j -v

CMakeLists.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ option(GRAPHQL_BUILD_MODULES "Build the C++20 module interface libraries." ON)
3737
option(GRAPHQL_BUILD_SCHEMAGEN "Build the schemagen tool." ON)
3838
option(GRAPHQL_BUILD_CLIENTGEN "Build the clientgen tool." ON)
3939
option(GRAPHQL_BUILD_TESTS "Build the tests and sample schema library." ON)
40-
option(GRAPHQL_USE_RAPIDJSON "Use RapidJSON for JSON serialization." ON)
40+
option(GRAPHQL_USE_TAOCPP_JSON "Use taocpp-json for JSON serialization." ON)
4141

4242
if(GRAPHQL_BUILD_SCHEMAGEN)
4343
list(APPEND VCPKG_MANIFEST_FEATURES "schemagen")
@@ -51,8 +51,13 @@ if(GRAPHQL_BUILD_TESTS)
5151
list(APPEND VCPKG_MANIFEST_FEATURES "tests")
5252
endif()
5353

54-
if(GRAPHQL_USE_RAPIDJSON)
55-
list(APPEND VCPKG_MANIFEST_FEATURES "rapidjson")
54+
if(GRAPHQL_USE_TAOCPP_JSON)
55+
list(APPEND VCPKG_MANIFEST_FEATURES "taocpp-json")
56+
else()
57+
option(GRAPHQL_USE_RAPIDJSON "Use RapidJSON for JSON serialization." ON)
58+
if(GRAPHQL_USE_RAPIDJSON)
59+
list(APPEND VCPKG_MANIFEST_FEATURES "rapidjson")
60+
endif()
5661
endif()
5762

5863
if(GRAPHQL_BUILD_SCHEMAGEN AND GRAPHQL_BUILD_CLIENTGEN)

src/CMakeLists.txt

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -387,21 +387,22 @@ if(WIN32 AND BUILD_SHARED_LIBS)
387387
add_version_rc(graphqlclient)
388388
endif()
389389

390-
# RapidJSON is the only option for JSON serialization used in this project, but if you want
391-
# to use another JSON library you can implement an alternate version of the functions in
392-
# JSONResponse.cpp to serialize to and from GraphQLResponse and build graphqljson from that.
393-
# You will also need to define how to build the graphqljson library target with your
394-
# implementation, and you should set BUILD_GRAPHQLJSON so that the test dependencies know
395-
# about your version of graphqljson.
396-
if(GRAPHQL_USE_RAPIDJSON)
390+
if(GRAPHQL_USE_TAOCPP_JSON)
391+
find_package(taocpp-json CONFIG REQUIRED)
392+
set(BUILD_GRAPHQLJSON ON)
393+
add_library(graphqljson TaoCppJSONResponse.cpp)
394+
target_link_libraries(graphqljson PRIVATE taocpp::json)
395+
elseif(GRAPHQL_USE_RAPIDJSON)
397396
find_package(RapidJSON CONFIG REQUIRED)
398-
399397
set(BUILD_GRAPHQLJSON ON)
400-
add_library(graphqljson JSONResponse.cpp)
398+
add_library(graphqljson RapidJSONResponse.cpp)
399+
target_include_directories(graphqljson SYSTEM PRIVATE ${RAPIDJSON_INCLUDE_DIRS})
400+
endif()
401+
402+
if(BUILD_GRAPHQLJSON)
401403
add_library(cppgraphqlgen::graphqljson ALIAS graphqljson)
402404
target_compile_features(graphqljson PUBLIC cxx_std_20)
403405
target_link_libraries(graphqljson PUBLIC graphqlresponse)
404-
target_include_directories(graphqljson SYSTEM PRIVATE ${RAPIDJSON_INCLUDE_DIRS})
405406
target_sources(graphqljson PUBLIC FILE_SET HEADERS
406407
BASE_DIRS ${INCLUDE_ROOT}
407408
FILES ${INCLUDE_ROOT}/graphqlservice/JSONResponse.h)
@@ -456,8 +457,6 @@ endif()
456457

457458
# graphqljson
458459
if(BUILD_GRAPHQLJSON)
459-
target_link_libraries(graphqljson PUBLIC graphqlresponse)
460-
461460
install(TARGETS graphqljson
462461
EXPORT cppgraphqlgen-targets
463462
RUNTIME DESTINATION bin

src/JSONResponse.cpp renamed to src/RapidJSONResponse.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616

1717
namespace graphql::response {
1818

19-
class StringWriter
19+
class StreamWriter
2020
{
2121
public:
22-
StringWriter(rapidjson::StringBuffer& buffer)
22+
StreamWriter(rapidjson::StringBuffer& buffer)
2323
: _writer { buffer }
2424
{
2525
}
@@ -81,7 +81,7 @@ class StringWriter
8181
std::string toJSON(Value&& response)
8282
{
8383
rapidjson::StringBuffer buffer;
84-
Writer writer { std::make_unique<StringWriter>(buffer) };
84+
Writer writer { std::make_unique<StreamWriter>(buffer) };
8585

8686
writer.write(std::move(response));
8787
return buffer.GetString();

src/TaoCppJSONResponse.cpp

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
#include "graphqlservice/JSONResponse.h"
5+
6+
#include <tao/json.hpp>
7+
8+
#include <cstdint>
9+
#include <iostream>
10+
#include <limits>
11+
#include <sstream>
12+
#include <vector>
13+
14+
namespace graphql::response {
15+
16+
class StreamWriter
17+
{
18+
public:
19+
StreamWriter(std::ostream& stream)
20+
: _writer { stream }
21+
{
22+
}
23+
24+
void start_object()
25+
{
26+
_scopeStack.push_back(Scope::Object);
27+
_writer.begin_object();
28+
}
29+
30+
void add_member(const std::string& key)
31+
{
32+
_writer.key(key);
33+
}
34+
35+
void end_object()
36+
{
37+
_writer.end_object();
38+
_scopeStack.pop_back();
39+
end_value();
40+
}
41+
42+
void start_array()
43+
{
44+
_scopeStack.push_back(Scope::Object);
45+
_writer.begin_array();
46+
}
47+
48+
void end_arrary()
49+
{
50+
_writer.end_array();
51+
_scopeStack.pop_back();
52+
end_value();
53+
}
54+
55+
void write_null()
56+
{
57+
_writer.null();
58+
end_value();
59+
}
60+
61+
void write_string(const std::string& value)
62+
{
63+
_writer.string(value);
64+
end_value();
65+
}
66+
67+
void write_bool(bool value)
68+
{
69+
_writer.boolean(value);
70+
end_value();
71+
}
72+
73+
void write_int(int value)
74+
{
75+
_writer.number(static_cast<std::int64_t>(value));
76+
end_value();
77+
}
78+
79+
void write_float(double value)
80+
{
81+
_writer.number(value);
82+
end_value();
83+
}
84+
85+
private:
86+
enum class Scope
87+
{
88+
Array,
89+
Object,
90+
};
91+
92+
void end_value()
93+
{
94+
if (_scopeStack.empty())
95+
{
96+
return;
97+
}
98+
99+
switch (_scopeStack.back())
100+
{
101+
case Scope::Array:
102+
_writer.element();
103+
break;
104+
105+
case Scope::Object:
106+
_writer.member();
107+
break;
108+
}
109+
}
110+
111+
tao::json::events::to_stream _writer;
112+
std::vector<Scope> _scopeStack;
113+
};
114+
115+
std::string toJSON(Value&& response)
116+
{
117+
std::ostringstream stream;
118+
Writer writer { std::make_unique<StreamWriter>(stream) };
119+
writer.write(std::move(response));
120+
return stream.str();
121+
}
122+
123+
struct ResponseHandler
124+
{
125+
ResponseHandler()
126+
{
127+
// Start with a single null value.
128+
_responseStack.push_back({});
129+
}
130+
131+
Value getResponse()
132+
{
133+
auto response = std::move(_responseStack.back());
134+
135+
_responseStack.pop_back();
136+
137+
return response;
138+
}
139+
140+
void null()
141+
{
142+
setValue(Value());
143+
}
144+
145+
void boolean(bool b)
146+
{
147+
setValue(Value(b));
148+
}
149+
150+
void number(double d)
151+
{
152+
auto value = Value(Type::Float);
153+
154+
value.set<FloatType>(std::move(d));
155+
setValue(std::move(value));
156+
}
157+
158+
void number(std::int64_t i)
159+
{
160+
if (i < std::numeric_limits<std::int32_t>::min()
161+
|| i > std::numeric_limits<std::int32_t>::max())
162+
{
163+
// https://spec.graphql.org/October2021/#sec-Int
164+
number(static_cast<double>(i));
165+
}
166+
else
167+
{
168+
static_assert(sizeof(std::int32_t) == sizeof(IntType),
169+
"GraphQL only supports 32-bit signed integers");
170+
auto value = Value(Type::Int);
171+
172+
value.set<IntType>(static_cast<std::int32_t>(i));
173+
setValue(std::move(value));
174+
}
175+
}
176+
177+
void number(std::uint64_t i)
178+
{
179+
if (i > static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max()))
180+
{
181+
// https://spec.graphql.org/October2021/#sec-Int
182+
number(static_cast<double>(i));
183+
}
184+
else
185+
{
186+
number(static_cast<std::int64_t>(i));
187+
}
188+
}
189+
190+
void string(std::string&& str)
191+
{
192+
setValue(Value(std::move(str)).from_json());
193+
}
194+
195+
void begin_array()
196+
{
197+
_responseStack.push_back(Value(Type::List));
198+
}
199+
200+
void element()
201+
{
202+
}
203+
204+
void end_array()
205+
{
206+
setValue(getResponse());
207+
}
208+
209+
void begin_object()
210+
{
211+
_responseStack.push_back(Value(Type::Map));
212+
}
213+
214+
void key(std::string&& str)
215+
{
216+
_keyStack.push_back(std::move(str));
217+
}
218+
219+
void member()
220+
{
221+
}
222+
223+
void end_object()
224+
{
225+
setValue(getResponse());
226+
}
227+
228+
private:
229+
void setValue(Value&& value)
230+
{
231+
switch (_responseStack.back().type())
232+
{
233+
case Type::Map:
234+
_responseStack.back().emplace_back(std::move(_keyStack.back()), std::move(value));
235+
_keyStack.pop_back();
236+
break;
237+
238+
case Type::List:
239+
_responseStack.back().emplace_back(std::move(value));
240+
break;
241+
242+
default:
243+
_responseStack.back() = std::move(value);
244+
break;
245+
}
246+
}
247+
248+
std::vector<std::string> _keyStack;
249+
std::vector<Value> _responseStack;
250+
};
251+
252+
Value parseJSON(const std::string& json)
253+
{
254+
ResponseHandler handler;
255+
tao::json::events::from_string(handler, json);
256+
257+
return handler.getResponse();
258+
}
259+
260+
} // namespace graphql::response

vcpkg.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,14 @@
2020
"gtest"
2121
]
2222
},
23+
"taocpp-json": {
24+
"description": "Build the graphqljson library with taocpp-json.",
25+
"dependencies": [
26+
"taocpp-json"
27+
]
28+
},
2329
"rapidjson": {
24-
"description": "Build the graphqljson library with RapidJSON.",
30+
"description": "Build the graphqljson library with rapidjson.",
2531
"dependencies": [
2632
"rapidjson"
2733
]

0 commit comments

Comments
 (0)