├── .clang-format ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .lgtm.yml ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── example ├── CMakeLists.txt └── jsonrpcpp_example.cpp ├── include ├── json.hpp └── jsonrpcpp.hpp └── test ├── CMakeLists.txt └── test_main.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AccessModifierOffset: '-4' 3 | AllowShortBlocksOnASingleLine: 'false' 4 | AllowShortCaseLabelsOnASingleLine: 'false' 5 | AllowShortFunctionsOnASingleLine: None 6 | AllowShortIfStatementsOnASingleLine: 'false' 7 | AllowShortLoopsOnASingleLine: 'false' 8 | AlwaysBreakTemplateDeclarations: 'true' 9 | BreakBeforeBraces: Allman 10 | ColumnLimit: '160' 11 | IndentCaseLabels: 'true' 12 | IndentWidth: '4' 13 | Language: Cpp 14 | MaxEmptyLinesToKeep: '3' 15 | PenaltyBreakComment: '100000' 16 | PointerAlignment: Left 17 | Standard: Cpp11 18 | UseTab: Never 19 | 20 | ... 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | linux: 8 | 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | compiler: 13 | - clang-10 14 | - clang-11 15 | - clang-12 16 | - clang-13 17 | - clang-14 18 | - clang-15 19 | - clang-16 20 | - clang-17 21 | - clang-18 22 | - gcc-9 23 | - gcc-10 24 | - gcc-11 25 | - gcc-12 26 | - gcc-13 27 | - gcc-14 28 | build-type: 29 | - Release 30 | - Debug 31 | include: 32 | - compiler: clang-10 33 | cc: clang-10 34 | cxx: clang++-10 35 | os: ubuntu-20.04 36 | - compiler: clang-11 37 | cc: clang-11 38 | cxx: clang++-11 39 | os: ubuntu-20.04 40 | - compiler: clang-12 41 | cc: clang-12 42 | cxx: clang++-12 43 | os: ubuntu-20.04 44 | - compiler: clang-13 45 | cc: clang-13 46 | cxx: clang++-13 47 | os: ubuntu-22.04 48 | - compiler: clang-14 49 | cc: clang-14 50 | cxx: clang++-14 51 | os: ubuntu-22.04 52 | - compiler: clang-15 53 | cc: clang-15 54 | cxx: clang++-15 55 | os: ubuntu-22.04 56 | - compiler: clang-16 57 | cc: clang-16 58 | cxx: clang++-16 59 | os: ubuntu-24.04 60 | - compiler: clang-17 61 | cc: clang-17 62 | cxx: clang++-17 63 | os: ubuntu-24.04 64 | - compiler: clang-18 65 | cc: clang-18 66 | cxx: clang++-18 67 | os: ubuntu-24.04 68 | - compiler: gcc-9 69 | cc: gcc-9 70 | cxx: g++-9 71 | os: ubuntu-22.04 72 | - compiler: gcc-10 73 | cc: gcc-10 74 | cxx: g++-10 75 | os: ubuntu-22.04 76 | - compiler: gcc-11 77 | cc: gcc-11 78 | cxx: g++-11 79 | os: ubuntu-22.04 80 | - compiler: gcc-12 81 | cc: gcc-12 82 | cxx: g++-12 83 | os: ubuntu-22.04 84 | - compiler: gcc-13 85 | cc: gcc-13 86 | cxx: g++-13 87 | os: ubuntu-24.04 88 | - compiler: gcc-14 89 | cc: gcc-14 90 | cxx: g++-14 91 | os: ubuntu-24.04 92 | 93 | runs-on: ${{ matrix.os }} 94 | 95 | steps: 96 | - uses: actions/checkout@v4 97 | - name: print environment 98 | run: env 99 | - name: configure 100 | run: cmake -S . -B build -DWERROR=ON -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} 101 | - name: build 102 | run: cmake --build build --parallel 8 --verbose 103 | env: 104 | CC: ${{ matrix.cc }} 105 | CXX: ${{ matrix.cxx }} 106 | - name: test 107 | run: ./build/test/jsonrpcpp_test 108 | 109 | macos: 110 | 111 | strategy: 112 | fail-fast: false 113 | matrix: 114 | xcode: 115 | - "13.1" 116 | - "13.2" 117 | - "13.3" 118 | - "13.4" 119 | - "14.0" 120 | - "14.1" 121 | - "14.2" 122 | - "14.3" 123 | - "15.0" 124 | - "15.1" 125 | - "15.2" 126 | - "15.3" 127 | - "15.4" 128 | build-type: 129 | - Release 130 | - Debug 131 | include: 132 | - xcode: "13.1" 133 | os: macos-12 134 | - xcode: "13.2" 135 | os: macos-12 136 | - xcode: "13.3" 137 | os: macos-12 138 | - xcode: "13.4" 139 | os: macos-12 140 | - xcode: "14.0" 141 | os: macos-12 142 | - xcode: "14.1" 143 | os: macos-13 144 | - xcode: "14.2" 145 | os: macos-13 146 | - xcode: "14.3" 147 | os: macos-14 148 | - xcode: "15.0" 149 | os: macos-14 150 | - xcode: "15.1" 151 | os: macos-14 152 | - xcode: "15.2" 153 | os: macos-14 154 | - xcode: "15.3" 155 | os: macos-14 156 | - xcode: "15.4" 157 | os: macos-14 158 | 159 | runs-on: ${{ matrix.os }} 160 | 161 | steps: 162 | - uses: actions/checkout@v4 163 | - name: print environment 164 | run: env 165 | - name: configure 166 | run: cmake -S . -B build -DWERROR=ON -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} 167 | env: 168 | DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer 169 | - name: build 170 | run: cmake --build build --parallel 8 --verbose 171 | - name: test 172 | run: ./build/test/jsonrpcpp_test 173 | 174 | 175 | windows: 176 | 177 | strategy: 178 | fail-fast: false 179 | matrix: 180 | compiler: 181 | - vs-16 182 | - vs-17 183 | build-type: 184 | - Release 185 | - Debug 186 | include: 187 | - compiler: vs-16 188 | os: windows-2019 189 | vs: "Visual Studio 16 2019" 190 | - compiler: vs-17 191 | os: windows-2022 192 | vs: "Visual Studio 17 2022" 193 | 194 | runs-on: ${{ matrix.os }} 195 | 196 | steps: 197 | - uses: actions/checkout@v4 198 | - name: configure 199 | run: | 200 | echo vcpkg installation root: ${env:VCPKG_INSTALLATION_ROOT} 201 | cmake -S . -B build -G "${{ matrix.vs }}" ` 202 | -DWERROR=ON ` 203 | -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" ` 204 | -DVCPKG_TARGET_TRIPLET="x64-windows" ` 205 | -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} `" 206 | - name: build 207 | run: cmake --build build --config ${{ matrix.build-type }} --parallel 8 --verbose 208 | - name: test 209 | run: .\build\test\${{ matrix.build-type }}\jsonrpcpp_test.exe 210 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | .cache 32 | 33 | jsonrpctest 34 | .vscode 35 | build 36 | jsonrpcpp_example 37 | -------------------------------------------------------------------------------- /.lgtm.yml: -------------------------------------------------------------------------------- 1 | queries: 2 | - exclude: cpp/use-of-goto -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | dist: trusty 3 | sudo: false 4 | 5 | matrix: 6 | include: 7 | # build on ubuntu 8 | - os: linux 9 | addons: 10 | apt: 11 | sources: 12 | - ubuntu-toolchain-r-test 13 | packages: 14 | - g++-4.9 15 | env: 16 | - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" 17 | 18 | - os: linux 19 | addons: 20 | apt: 21 | sources: 22 | - ubuntu-toolchain-r-test 23 | packages: 24 | - g++-5 25 | env: 26 | - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" 27 | 28 | - os: linux 29 | addons: 30 | apt: 31 | sources: 32 | - ubuntu-toolchain-r-test 33 | packages: 34 | - g++-6 35 | env: 36 | - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" 37 | 38 | - os: linux 39 | addons: 40 | apt: 41 | sources: 42 | - ubuntu-toolchain-r-test 43 | packages: 44 | - g++-7 45 | env: 46 | - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" 47 | 48 | # works on Precise and Trusty 49 | - os: linux 50 | addons: 51 | apt: 52 | sources: 53 | - ubuntu-toolchain-r-test 54 | - llvm-toolchain-precise-3.6 55 | packages: 56 | - clang-3.6 57 | env: 58 | - MATRIX_EVAL="CC=clang-3.6 && CXX=clang++-3.6" 59 | 60 | # works on Precise and Trusty 61 | - os: linux 62 | addons: 63 | apt: 64 | sources: 65 | - ubuntu-toolchain-r-test 66 | - llvm-toolchain-precise-3.7 67 | packages: 68 | - clang-3.7 69 | env: 70 | - MATRIX_EVAL="CC=clang-3.7 && CXX=clang++-3.7" 71 | 72 | # works on Precise and Trusty 73 | - os: linux 74 | addons: 75 | apt: 76 | sources: 77 | - ubuntu-toolchain-r-test 78 | - llvm-toolchain-precise-3.8 79 | packages: 80 | - clang-3.8 81 | env: 82 | - MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" 83 | 84 | # works on Trusty 85 | - os: linux 86 | addons: 87 | apt: 88 | sources: 89 | - llvm-toolchain-trusty-3.9 90 | packages: 91 | - clang-3.9 92 | env: 93 | - MATRIX_EVAL="CC=clang-3.9 && CXX=clang++-3.9" 94 | 95 | # works on Trusty 96 | - os: linux 97 | addons: 98 | apt: 99 | sources: 100 | - llvm-toolchain-trusty-4.0 101 | packages: 102 | - clang-4.0 103 | env: 104 | - MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0" 105 | 106 | # works on Trusty 107 | - os: linux 108 | addons: 109 | apt: 110 | sources: 111 | - llvm-toolchain-trusty-5.0 112 | packages: 113 | - clang-5.0 114 | env: 115 | - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0" 116 | 117 | # build on osx 118 | - os: osx 119 | osx_image: xcode9.4 120 | 121 | - os: osx 122 | osx_image: xcode10.3 123 | 124 | - os: osx 125 | osx_image: xcode11 126 | 127 | before_install: 128 | - eval "${MATRIX_EVAL}" 129 | 130 | script: 131 | - mkdir build 132 | - cd build 133 | - cmake .. && make && ./example/jsonrpcpp_example && ./test/jsonrpcpp_test 134 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # __ ____ __ __ _ ____ ____ ___ _ _ 2 | # _( )/ ___) / \ ( ( \( _ \( _ \ / __)( ) ( ) 3 | # / \) \\___ \( O )/ / ) / ) __/( (__(_ _)(_ _) 4 | # \____/(____/ \__/ \_)__)(__\_)(__) \___)(_) (_) 5 | 6 | # This file is part of jsonrpc++ 7 | # Copyright (C) 2017-2024 Johannes Pohl 8 | 9 | # This software may be modified and distributed under the terms 10 | # of the MIT license. See the LICENSE file for details. 11 | 12 | 13 | cmake_minimum_required(VERSION 3.14) 14 | 15 | project(jsonrpcpp VERSION 1.4.0 LANGUAGES CXX) 16 | set(PROJECT_DESCRIPTION "C++ JSON-RPC 2.0 library") 17 | set(PROJECT_URL "https://github.com/badaix/jsonrpcpp") 18 | 19 | option(BUILD_EXAMPLE "Build example (build jsonrpcpp_example demo)" ON) 20 | option(BUILD_TESTS "Build tests" ON) 21 | option(WERROR "Treat warnings as errors" OFF) 22 | 23 | set(CMAKE_CXX_STANDARD 11) 24 | set(CMAKE_CXX_EXTENSIONS OFF) 25 | 26 | 27 | if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) 28 | SET(CMAKE_INSTALL_INCLUDEDIR include CACHE 29 | PATH "Output directory for header files") 30 | endif() 31 | 32 | include_directories( 33 | "include" 34 | ) 35 | 36 | install(FILES include/jsonrpcpp.hpp include/json.hpp 37 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/jsonrpcpp") 38 | 39 | if(MSVC) 40 | # warning level 4 and all warnings as errors warning C4505: 'getArch': 41 | # unreferenced local function has been removed warning C4458: declaration of 42 | # 'size' hides class member warning C4459: declaration of 'query' hides global 43 | # declaration 44 | add_compile_options(/W4 /wd4458 /wd4459 /wd4505) 45 | if(WERROR) 46 | add_compile_options(/WX) 47 | endif() 48 | else() 49 | # lots of warnings and all warnings as errors 50 | add_compile_options(-Wall -Wextra -pedantic) 51 | 52 | if(WERROR) 53 | add_compile_options(-Werror) 54 | endif() 55 | endif() 56 | 57 | 58 | if (BUILD_EXAMPLE) 59 | add_subdirectory(example) 60 | endif (BUILD_EXAMPLE) 61 | 62 | if (BUILD_TESTS) 63 | add_subdirectory(test) 64 | endif (BUILD_TESTS) 65 | 66 | FIND_PROGRAM(CLANG_FORMAT "clang-format") 67 | IF(CLANG_FORMAT) 68 | set(CHECK_CXX_SOURCE_FILES 69 | ${CMAKE_SOURCE_DIR}/include/jsonrpcpp.hpp 70 | ${CMAKE_SOURCE_DIR}/example/jsonrpcpp_example.cpp 71 | ${CMAKE_SOURCE_DIR}/test/test_main.cpp 72 | ) 73 | 74 | list(REMOVE_ITEM CHECK_CXX_SOURCE_FILES "${CMAKE_SOURCE_DIR}/common/json.hpp") 75 | 76 | ADD_CUSTOM_TARGET( 77 | reformat 78 | COMMAND 79 | ${CLANG_FORMAT} 80 | -i 81 | -style=file 82 | ${CHECK_CXX_SOURCE_FILES} 83 | COMMENT "Auto formatting of all source files" 84 | ) 85 | ENDIF() 86 | 87 | 88 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2024 Johannes Pohl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BIN = jsonrpcpp_example 2 | 3 | CXX = clang++ 4 | STRIP = strip 5 | CXXFLAGS = -std=c++11 -Wall -O3 -Iinclude -pedantic -Wextra -Wshadow -Wconversion 6 | 7 | OBJ = example/jsonrpcpp_example.o 8 | 9 | reformat: 10 | clang-format -i include/jsonrpcpp.hpp 11 | clang-format -i example/jsonrpcpp_example.cpp 12 | clang-format -i test/test_main.cpp 13 | 14 | all: $(OBJ) 15 | $(CXX) $(CXXFLAGS) -o $(BIN) $(OBJ) $(LDFLAGS) 16 | $(STRIP) $(BIN) 17 | 18 | %.o: %.cpp 19 | $(CXX) $(CXXFLAGS) -c $< -o $@ 20 | 21 | clean: 22 | rm -rf $(BIN) $(OBJ) *~ 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jsonrpc++ 2 | 3 | Leightweight C++ [JSON-RPC 2.0](http://www.jsonrpc.org/specification) library 4 | 5 | [![Github Releases](https://img.shields.io/github/release/badaix/jsonrpcpp.svg)](https://github.com/badaix/jsonrpcpp/releases) 6 | [![Build Status](https://travis-ci.org/badaix/jsonrpcpp.svg?branch=master)](https://travis-ci.org/badaix/jsonrpcpp) 7 | [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/badaix/jsonrpcpp.svg)](https://lgtm.com/projects/g/badaix/jsonrpcpp/context:cpp) 8 | 9 | ## What it is 10 | 11 | jsonrpc++ parses and constructs [JSON-RPC 2.0](https://www.jsonrpc.org/specification) objects, like 12 | 13 | * [Request](http://www.jsonrpc.org/specification#request_object) 14 | * [Notification](http://www.jsonrpc.org/specification#notification) 15 | * [Parameter](http://www.jsonrpc.org/specification#parameter_structures) 16 | * [Response](http://www.jsonrpc.org/specification#response_object) 17 | * [Error](http://www.jsonrpc.org/specification#error_object) 18 | * [Batch](http://www.jsonrpc.org/specification#batch) 19 | 20 | ### Example: Parsing a request 21 | 22 | ```c++ 23 | jsonrpcpp::entity_ptr entity = jsonrpcpp::Parser::do_parse(R"({"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3})"); 24 | if (entity->is_request()) 25 | { 26 | jsonrpcpp::request_ptr request = dynamic_pointer_cast(entity); 27 | if (request->method() == "subtract") 28 | { 29 | int result = request->params().get("minuend") - request->params().get("subtrahend"); 30 | jsonrpcpp::Response response(*request, result); 31 | cout << " Response: " << response.to_json().dump() << "\n"; 32 | //will print: {"jsonrpc": "2.0", "result": 19, "id": 3} 33 | } 34 | else 35 | throw jsonrpcpp::MethodNotFoundException(*request); 36 | } 37 | ``` 38 | 39 | ## What it not is 40 | 41 | jsonrpc++ is completely transport agnostic, i.e. it doesn't care about transportation of the messages and there is no TCP client or server component shipped with this library. 42 | 43 | As JSON backbone [JSON for Modern C++](https://nlohmann.github.io/json/) is used. 44 | 45 | ## Some code example 46 | 47 | ```c++ 48 | jsonrpcpp::entity_ptr entity = 49 | jsonrpcpp::Parser::do_parse(R"({"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3})"); 50 | if (entity && entity->is_request()) 51 | { 52 | jsonrpcpp::request_ptr request = dynamic_pointer_cast(entity); 53 | cout << " Request: " << request->method() << ", id: " << request->id() << ", has params: " << !request->params().is_null() << "\n"; 54 | if (request->method() == "subtract") 55 | { 56 | int result; 57 | if (request->params().is_array()) 58 | result = request->params().get(0) - request->params().get(1); 59 | else 60 | result = request->params().get("minuend") - request->params().get("subtrahend"); 61 | 62 | jsonrpcpp::Response response(*request, result); 63 | cout << " Response: " << response.to_json().dump() << "\n"; 64 | } 65 | else if (request->method() == "sum") 66 | { 67 | int result = 0; 68 | for (const auto& summand : request->params().param_array) 69 | result += summand.get(); 70 | jsonrpcpp::Response response(*request, result); 71 | cout << " Response: " << response.to_json().dump() << "\n"; 72 | } 73 | else 74 | { 75 | throw jsonrpcpp::MethodNotFoundException(*request); 76 | } 77 | } 78 | ``` 79 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(jsonrpcpp_example jsonrpcpp_example.cpp) 2 | -------------------------------------------------------------------------------- /example/jsonrpcpp_example.cpp: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of jsonrpc++ 3 | Copyright (C) 2017-2024 Johannes Pohl 4 | 5 | This software may be modified and distributed under the terms 6 | of the MIT license. See the LICENSE file for details. 7 | ***/ 8 | 9 | 10 | #include "jsonrpcpp.hpp" 11 | #include 12 | 13 | using namespace std; 14 | 15 | 16 | jsonrpcpp::Parser parser; 17 | 18 | 19 | jsonrpcpp::Response getResponse(jsonrpcpp::request_ptr request) 20 | { 21 | // cout << " Request: " << request->method << ", id: " << request->id << ", has params: " << !request->params().is_null() << "\n"; 22 | if (request->method() == "subtract") 23 | { 24 | if (request->params()) 25 | { 26 | int result; 27 | if (request->params().is_array()) 28 | result = request->params().get(0) - request->params().get(1); 29 | else 30 | result = request->params().get("minuend") - request->params().get("subtrahend"); 31 | 32 | return jsonrpcpp::Response(*request, result); 33 | } 34 | throw jsonrpcpp::InvalidParamsException(*request); 35 | } 36 | else if (request->method() == "sum") 37 | { 38 | int result = 0; 39 | for (const auto& summand : request->params().param_array) 40 | result += summand.get(); 41 | return jsonrpcpp::Response(*request, result); 42 | } 43 | else if (request->method() == "get_data") 44 | { 45 | return jsonrpcpp::Response(*request, Json({"hello", 5})); 46 | } 47 | else 48 | { 49 | throw jsonrpcpp::MethodNotFoundException(*request); 50 | } 51 | } 52 | 53 | 54 | 55 | void test(const std::string& json_str) 56 | { 57 | try 58 | { 59 | cout << "--> " << json_str << "\n"; 60 | jsonrpcpp::entity_ptr entity = parser.parse(json_str); 61 | if (entity) 62 | { 63 | // cout << " Json: " << entity->to_json().dump() << "\n"; 64 | if (entity->is_response()) 65 | { 66 | cout << "<-- " << entity->to_json().dump() << "\n"; 67 | } 68 | if (entity->is_request()) 69 | { 70 | jsonrpcpp::Response response = getResponse(dynamic_pointer_cast(entity)); 71 | cout << "<-- " << response.to_json().dump() << "\n"; 72 | } 73 | else if (entity->is_notification()) 74 | { 75 | jsonrpcpp::notification_ptr notification = dynamic_pointer_cast(entity); 76 | cout << "Notification: " << notification->method() << ", has params: " << !notification->params().is_null() << "\n"; 77 | } 78 | else if (entity->is_batch()) 79 | { 80 | jsonrpcpp::batch_ptr batch = dynamic_pointer_cast(entity); 81 | jsonrpcpp::Batch responseBatch; 82 | // cout << " Batch\n"; 83 | for (const auto& batch_entity : batch->entities) 84 | { 85 | // cout << batch_entity->type_str() << ": \t" << batch_entity->to_json() << "\n"; 86 | if (batch_entity->is_request()) 87 | { 88 | try 89 | { 90 | jsonrpcpp::Response response = getResponse(dynamic_pointer_cast(batch_entity)); 91 | responseBatch.add(response); // 92 | } 93 | catch (const jsonrpcpp::RequestException& e) 94 | { 95 | responseBatch.add(e); // 96 | } 97 | } 98 | else if (batch_entity->is_exception()) 99 | { 100 | responseBatch.add_ptr(batch_entity); 101 | } 102 | else if (batch_entity->is_error()) 103 | { 104 | jsonrpcpp::error_ptr error = dynamic_pointer_cast(batch_entity); 105 | responseBatch.add(jsonrpcpp::RequestException(*error)); 106 | } 107 | } 108 | if (!responseBatch.entities.empty()) 109 | cout << "<-- " << responseBatch.to_json().dump() << "\n"; 110 | } 111 | } 112 | } 113 | catch (const jsonrpcpp::RequestException& e) 114 | { 115 | cout << "<-- " << e.to_json().dump() << "\n"; 116 | // cout << " Response: " << jsonrpcpp::Response(e).to_json().dump() << "\n"; 117 | // cerr << "RequestException: " << e.what() << "\n"; 118 | } 119 | catch (const jsonrpcpp::ParseErrorException& e) 120 | { 121 | cout << "<-- " << e.to_json().dump() << "\n"; 122 | } 123 | catch (const jsonrpcpp::RpcException& e) 124 | { 125 | cerr << "RpcException: " << e.what() << "\n"; 126 | cout << "<-- " << jsonrpcpp::ParseErrorException(e.what()).to_json().dump() << "\n"; 127 | } 128 | catch (const std::exception& e) 129 | { 130 | cerr << "Exception: " << e.what() << "\n"; 131 | } 132 | cout << "\n"; 133 | } 134 | 135 | 136 | void test(const jsonrpcpp::Entity& entity) 137 | { 138 | test(entity.to_json().dump()); 139 | } 140 | 141 | 142 | void update(const jsonrpcpp::Parameter& params) 143 | { 144 | cout << "Notification callback: update, has params: " << !params.is_null() << "\n"; 145 | } 146 | 147 | /* 148 | void foobar(const jsonrpcpp::Notification& notification, const jsonrpcpp::Parameter& params) 149 | { 150 | cout << "Notification callback: " << notification.method << ", has params: " << !notification.params().is_null() << "\n"; 151 | } 152 | */ 153 | 154 | void foobar(const jsonrpcpp::Parameter& params) 155 | { 156 | cout << "Notification callback: foobar, has params: " << !params.is_null() << "\n"; 157 | } 158 | 159 | 160 | jsonrpcpp::response_ptr sum(const jsonrpcpp::Id& id, const jsonrpcpp::Parameter& params) 161 | { 162 | int result = 0; 163 | for (const auto& summand : params.param_array) 164 | result += summand.get(); 165 | cout << "Request callback: sum, result: " << result << "\n"; 166 | return make_shared(id, result); 167 | } 168 | 169 | 170 | // examples taken from: http://www.jsonrpc.org/specification#examples 171 | int main(int /*argc*/, char** /*argv*/) 172 | { 173 | parser.register_notification_callback("update", update); 174 | parser.register_notification_callback("foobar", foobar); 175 | parser.register_request_callback("sum", sum); 176 | 177 | cout << "rpc call with positional parameters:\n\n"; 178 | test(R"({"jsonrpc": "2.0", "method": "sum", "params": [1, 2, 3, 4, 5], "id": 1})"); 179 | test(jsonrpcpp::Request(1, "sum", Json({1, 2, 3, 4, 5}))); 180 | 181 | test(R"({"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1})"); 182 | test(jsonrpcpp::Request(1, "subtract", Json({42, 23}))); 183 | test(R"({"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 2})"); 184 | test(jsonrpcpp::Request(2, "subtract", Json({23, 42}))); 185 | 186 | cout << "\n\nrpc call with named parameters:\n\n"; 187 | test(R"({"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3})"); 188 | test(jsonrpcpp::Request(3, "subtract", Json({{"subtrahend", 23}, {"minuend", 42}}))); 189 | test(R"({"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4})"); 190 | test(jsonrpcpp::Request(4, "subtract", Json({{"minuend", 42}, {"subtrahend", 23}}))); 191 | 192 | cout << "\n\na Notification:\n\n"; 193 | test(R"({"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]})"); 194 | test(jsonrpcpp::Notification("update", Json({1, 2, 3, 4, 5}))); 195 | test(R"({"jsonrpc": "2.0", "method": "foobar"})"); 196 | test(jsonrpcpp::Notification("foobar")); 197 | 198 | cout << "\n\nrpc call of non-existent method:\n\n"; 199 | test(R"({"jsonrpc": "2.0", "method": "foobar", "id": "1"})"); 200 | test(jsonrpcpp::Request("1", "foobar")); 201 | 202 | cout << "\n\nrpc call with invalid JSON:\n\n"; 203 | test(R"({"jsonrpc": "2.0", "method": "foobar, "params": "bar", "baz])"); 204 | 205 | cout << "\n\nrpc call with invalid Request object:\n\n"; 206 | test(R"({"jsonrpc": "2.0", "method": 1, "params": "bar"})"); 207 | 208 | cout << "\n\nrpc call Batch, invalid JSON:\n\n"; 209 | test(R"( [ 210 | {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}, 211 | {"jsonrpc": "2.0", "method" 212 | ])"); 213 | 214 | cout << "\n\nrpc call with an empty Array:\n\n"; 215 | test(R"([])"); 216 | 217 | cout << "\n\nrpc call with an invalid Batch (but not empty):\n\n"; 218 | test(R"([1])"); 219 | 220 | cout << "\n\nrpc call with invalid Batch:\n\n"; 221 | test(R"([1,2,3])"); 222 | 223 | cout << "\n\nrpc call Batch:\n\n"; 224 | test(R"( [ 225 | {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}, 226 | {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]}, 227 | {"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"}, 228 | {"foo": "boo"}, 229 | {"jsonrpc": "2.0", "method": 1, "params": "bar"}, 230 | {"jsonrpc": "2.0", "method": 1, "params": "bar", "id": 4}, 231 | {"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"}, 232 | {"jsonrpc": "2.0", "method": "get_data", "id": "9"} 233 | ])"); 234 | 235 | cout << "\n\nrpc call Batch (all notifications):\n\n"; 236 | test(R"( [ 237 | {"jsonrpc": "2.0", "method": "notify_sum", "params": [1,2,4]}, 238 | {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]} 239 | ])"); 240 | } 241 | -------------------------------------------------------------------------------- /include/jsonrpcpp.hpp: -------------------------------------------------------------------------------- 1 | /*** 2 | __ ____ __ __ _ ____ ____ ___ _ _ 3 | _( )/ ___) / \ ( ( \( _ \( _ \ / __)( ) ( ) 4 | / \) \\___ \( O )/ / ) / ) __/( (__(_ _)(_ _) 5 | \____/(____/ \__/ \_)__)(__\_)(__) \___)(_) (_) 6 | version 1.4.0 7 | https://github.com/badaix/jsonrpcpp 8 | 9 | This file is part of jsonrpc++ 10 | Copyright (C) 2017-2024 Johannes Pohl 11 | 12 | This software may be modified and distributed under the terms 13 | of the MIT license. See the LICENSE file for details. 14 | ***/ 15 | 16 | /// http://patorjk.com/software/taag/#p=display&f=Graceful&t=JSONRPC%2B%2B 17 | 18 | /// checked with clang-tidy: 19 | /// run-clang-tidy-3.8.py -header-filter='jsonrpcpp.hpp' 20 | /// -checks='*,-misc-definitions-in-headers,-google-readability-braces-around-statements,-readability-braces-around-statements,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-google-build-using-namespace,-google-build-using-namespace,-modernize-pass-by-value,-google-explicit-constructor' 21 | 22 | #ifndef JSON_RPC_HPP 23 | #define JSON_RPC_HPP 24 | 25 | // nlohmann-json 26 | #include 27 | 28 | // standard headers 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | 35 | using Json = nlohmann::json; 36 | 37 | namespace jsonrpcpp 38 | { 39 | 40 | class Entity; 41 | class Request; 42 | class Notification; 43 | class Parameter; 44 | class Response; 45 | class Error; 46 | class Batch; 47 | 48 | using entity_ptr = std::shared_ptr; 49 | using request_ptr = std::shared_ptr; 50 | using notification_ptr = std::shared_ptr; 51 | using parameter_ptr = std::shared_ptr; 52 | using response_ptr = std::shared_ptr; 53 | using error_ptr = std::shared_ptr; 54 | using batch_ptr = std::shared_ptr; 55 | 56 | 57 | class Entity 58 | { 59 | public: 60 | enum class entity_t : uint8_t 61 | { 62 | unknown, 63 | exception, 64 | id, 65 | error, 66 | response, 67 | request, 68 | notification, 69 | batch 70 | }; 71 | 72 | Entity(entity_t type); 73 | virtual ~Entity() = default; 74 | Entity(const Entity&) = default; 75 | Entity& operator=(const Entity&) = default; 76 | 77 | bool is_exception() const; 78 | bool is_id() const; 79 | bool is_error() const; 80 | bool is_response() const; 81 | bool is_request() const; 82 | bool is_notification() const; 83 | bool is_batch() const; 84 | 85 | virtual std::string type_str() const; 86 | 87 | virtual Json to_json() const = 0; 88 | virtual void parse_json(const Json& json) = 0; 89 | 90 | virtual void parse(const std::string& json_str); 91 | virtual void parse(const char* json_str); 92 | 93 | protected: 94 | entity_t entity; 95 | }; 96 | 97 | 98 | class NullableEntity : public Entity 99 | { 100 | public: 101 | NullableEntity(entity_t type); 102 | NullableEntity(entity_t type, std::nullptr_t); 103 | #ifdef _MSC_VER 104 | virtual operator bool() const 105 | #else 106 | virtual explicit operator bool() const 107 | #endif 108 | { 109 | return !isNull; 110 | } 111 | 112 | protected: 113 | bool isNull; 114 | }; 115 | 116 | 117 | class Id : public Entity 118 | { 119 | public: 120 | enum class value_t : uint8_t 121 | { 122 | null, 123 | string, 124 | integer 125 | }; 126 | 127 | Id(); 128 | Id(int id); 129 | Id(const char* id); 130 | Id(const std::string& id); 131 | Id(const Json& json_id); 132 | 133 | Json to_json() const override; 134 | void parse_json(const Json& json) override; 135 | 136 | friend std::ostream& operator<<(std::ostream& out, const Id& id) 137 | { 138 | out << id.to_json(); 139 | return out; 140 | } 141 | 142 | const value_t& type() const 143 | { 144 | return type_; 145 | } 146 | 147 | int int_id() const 148 | { 149 | return int_id_; 150 | } 151 | 152 | const std::string& string_id() const 153 | { 154 | return string_id_; 155 | } 156 | 157 | bool operator<(const Id& other) const 158 | { 159 | return (to_json() < other.to_json()); 160 | } 161 | 162 | protected: 163 | value_t type_; 164 | int int_id_; 165 | std::string string_id_; 166 | }; 167 | 168 | 169 | class Parameter : public NullableEntity 170 | { 171 | public: 172 | enum class value_t : uint8_t 173 | { 174 | null, 175 | array, 176 | map 177 | }; 178 | 179 | Parameter(std::nullptr_t); 180 | Parameter(const Json& json = nullptr); 181 | Parameter(const std::string& key1, const Json& value1, const std::string& key2 = "", const Json& value2 = nullptr, const std::string& key3 = "", 182 | const Json& value3 = nullptr, const std::string& key4 = "", const Json& value4 = nullptr); 183 | 184 | Json to_json() const override; 185 | void parse_json(const Json& json) override; 186 | 187 | bool is_array() const; 188 | bool is_map() const; 189 | bool is_null() const; 190 | 191 | Json get(const std::string& key) const; 192 | Json get(size_t idx) const; 193 | bool has(const std::string& key) const; 194 | bool has(size_t idx) const; 195 | 196 | void add(const std::string& key, const Json& value); 197 | 198 | template 199 | T get(const std::string& key) const 200 | { 201 | return get(key).get(); 202 | } 203 | 204 | template 205 | T get(size_t idx) const 206 | { 207 | return get(idx).get(); 208 | } 209 | 210 | template 211 | T get(const std::string& key, const T& default_value) const 212 | { 213 | if (!has(key)) 214 | return default_value; 215 | return get(key); 216 | } 217 | 218 | template 219 | T get(size_t idx, const T& default_value) const 220 | { 221 | if (!has(idx)) 222 | return default_value; 223 | return get(idx); 224 | } 225 | 226 | value_t type; 227 | std::vector param_array; 228 | std::map param_map; 229 | }; 230 | 231 | 232 | class Error : public NullableEntity 233 | { 234 | public: 235 | Error(const Json& json = nullptr); 236 | Error(std::nullptr_t); 237 | Error(const std::string& message, int code, const Json& data = nullptr); 238 | 239 | Json to_json() const override; 240 | void parse_json(const Json& json) override; 241 | 242 | int code() const 243 | { 244 | return code_; 245 | } 246 | 247 | const std::string& message() const 248 | { 249 | return message_; 250 | } 251 | 252 | const Json& data() const 253 | { 254 | return data_; 255 | } 256 | 257 | protected: 258 | int code_; 259 | std::string message_; 260 | Json data_; 261 | }; 262 | 263 | 264 | /// JSON-RPC 2.0 request 265 | /** 266 | * Simple jsonrpc 2.0 parser with getters 267 | * Currently no named parameters are supported, but only array parameters 268 | */ 269 | class Request : public Entity 270 | { 271 | public: 272 | Request(const Json& json = nullptr); 273 | Request(const Id& id, const std::string& method, const Parameter& params = nullptr); 274 | 275 | Json to_json() const override; 276 | void parse_json(const Json& json) override; 277 | 278 | const std::string& method() const 279 | { 280 | return method_; 281 | } 282 | 283 | const Parameter& params() const 284 | { 285 | return params_; 286 | } 287 | 288 | const Id& id() const 289 | { 290 | return id_; 291 | } 292 | 293 | protected: 294 | std::string method_; 295 | Parameter params_; 296 | Id id_; 297 | }; 298 | 299 | 300 | class RpcException : public std::exception 301 | { 302 | public: 303 | RpcException(const char* text); 304 | RpcException(const std::string& text); 305 | 306 | const char* what() const noexcept override; 307 | 308 | protected: 309 | std::runtime_error m_; 310 | }; 311 | 312 | 313 | class RpcEntityException : public RpcException, public Entity 314 | { 315 | public: 316 | RpcEntityException(const Error& error); 317 | RpcEntityException(const std::string& text); 318 | Json to_json() const override = 0; 319 | 320 | const Error& error() const 321 | { 322 | return error_; 323 | } 324 | 325 | protected: 326 | void parse_json(const Json& json) override; 327 | Error error_; 328 | }; 329 | 330 | 331 | class ParseErrorException : public RpcEntityException 332 | { 333 | public: 334 | ParseErrorException(const Error& error); 335 | ParseErrorException(const std::string& data); 336 | Json to_json() const override; 337 | }; 338 | 339 | 340 | // -32600 Invalid Request The JSON sent is not a valid Request object. 341 | // -32601 Method not found The method does not exist / is not available. 342 | // -32602 Invalid params Invalid method parameter(s). 343 | // -32603 Internal error Internal JSON-RPC error. 344 | 345 | class RequestException : public RpcEntityException 346 | { 347 | public: 348 | RequestException(const Error& error, const Id& requestId = Id()); 349 | Json to_json() const override; 350 | 351 | const Id& id() const 352 | { 353 | return id_; 354 | } 355 | 356 | protected: 357 | Id id_; 358 | }; 359 | 360 | 361 | class InvalidRequestException : public RequestException 362 | { 363 | public: 364 | InvalidRequestException(const Id& requestId = Id()); 365 | InvalidRequestException(const Request& request); 366 | InvalidRequestException(const char* data, const Id& requestId = Id()); 367 | InvalidRequestException(const std::string& data, const Id& requestId = Id()); 368 | }; 369 | 370 | 371 | class MethodNotFoundException : public RequestException 372 | { 373 | public: 374 | MethodNotFoundException(const Id& requestId = Id()); 375 | MethodNotFoundException(const Request& request); 376 | MethodNotFoundException(const char* data, const Id& requestId = Id()); 377 | MethodNotFoundException(const std::string& data, const Id& requestId = Id()); 378 | }; 379 | 380 | 381 | class InvalidParamsException : public RequestException 382 | { 383 | public: 384 | InvalidParamsException(const Id& requestId = Id()); 385 | InvalidParamsException(const Request& request); 386 | InvalidParamsException(const char* data, const Id& requestId = Id()); 387 | InvalidParamsException(const std::string& data, const Id& requestId = Id()); 388 | }; 389 | 390 | 391 | class InternalErrorException : public RequestException 392 | { 393 | public: 394 | InternalErrorException(const Id& requestId = Id()); 395 | InternalErrorException(const Request& request); 396 | InternalErrorException(const char* data, const Id& requestId = Id()); 397 | InternalErrorException(const std::string& data, const Id& requestId = Id()); 398 | }; 399 | 400 | 401 | class Response : public Entity 402 | { 403 | public: 404 | Response(const Json& json = nullptr); 405 | Response(const Id& id, const Json& result); 406 | Response(const Id& id, const Error& error); 407 | Response(const Request& request, const Json& result); 408 | Response(const Request& request, const Error& error); 409 | Response(const RequestException& exception); 410 | 411 | Json to_json() const override; 412 | void parse_json(const Json& json) override; 413 | 414 | const Id& id() const 415 | { 416 | return id_; 417 | } 418 | 419 | const Json& result() const 420 | { 421 | return result_; 422 | } 423 | 424 | const Error& error() const 425 | { 426 | return error_; 427 | } 428 | 429 | protected: 430 | Id id_; 431 | Json result_; 432 | Error error_; 433 | }; 434 | 435 | 436 | class Notification : public Entity 437 | { 438 | public: 439 | Notification(const Json& json = nullptr); 440 | Notification(const char* method, const Parameter& params = nullptr); 441 | Notification(const std::string& method, const Parameter& params); 442 | 443 | Json to_json() const override; 444 | void parse_json(const Json& json) override; 445 | 446 | const std::string& method() const 447 | { 448 | return method_; 449 | } 450 | 451 | const Parameter& params() const 452 | { 453 | return params_; 454 | } 455 | 456 | protected: 457 | std::string method_; 458 | Parameter params_; 459 | }; 460 | 461 | 462 | typedef std::function notification_callback; 463 | typedef std::function request_callback; 464 | 465 | class Parser 466 | { 467 | public: 468 | Parser() = default; 469 | virtual ~Parser() = default; 470 | 471 | entity_ptr parse(const std::string& json_str); 472 | entity_ptr parse_json(const Json& json); 473 | 474 | void register_notification_callback(const std::string& notification, notification_callback callback); 475 | void register_request_callback(const std::string& request, request_callback callback); 476 | 477 | static entity_ptr do_parse(const std::string& json_str); 478 | static entity_ptr do_parse_json(const Json& json); 479 | static bool is_request(const std::string& json_str); 480 | static bool is_request(const Json& json); 481 | static bool is_notification(const std::string& json_str); 482 | static bool is_notification(const Json& json); 483 | static bool is_response(const std::string& json_str); 484 | static bool is_response(const Json& json); 485 | static bool is_batch(const std::string& json_str); 486 | static bool is_batch(const Json& json); 487 | 488 | private: 489 | std::map notification_callbacks_; 490 | std::map request_callbacks_; 491 | }; 492 | 493 | 494 | class Batch : public Entity 495 | { 496 | public: 497 | std::vector entities; 498 | 499 | Batch(const Json& json = nullptr); 500 | 501 | Json to_json() const override; 502 | void parse_json(const Json& json) override; 503 | 504 | template 505 | void add(const T& entity) 506 | { 507 | entities.push_back(std::make_shared(entity)); 508 | } 509 | 510 | void add_ptr(const entity_ptr& entity) 511 | { 512 | entities.push_back(entity); 513 | } 514 | }; 515 | 516 | 517 | 518 | /////////////////////////// Entity implementation ///////////////////////////// 519 | 520 | inline Entity::Entity(entity_t type) : entity(type) 521 | { 522 | } 523 | 524 | inline bool Entity::is_exception() const 525 | { 526 | return (entity == entity_t::exception); 527 | } 528 | 529 | inline bool Entity::is_id() const 530 | { 531 | return (entity == entity_t::id); 532 | } 533 | 534 | inline bool Entity::is_error() const 535 | { 536 | return (entity == entity_t::error); 537 | } 538 | 539 | inline bool Entity::is_response() const 540 | { 541 | return (entity == entity_t::response); 542 | } 543 | 544 | inline bool Entity::is_request() const 545 | { 546 | return (entity == entity_t::request); 547 | } 548 | 549 | inline bool Entity::is_notification() const 550 | { 551 | return (entity == entity_t::notification); 552 | } 553 | 554 | inline bool Entity::is_batch() const 555 | { 556 | return (entity == entity_t::batch); 557 | } 558 | 559 | inline void Entity::parse(const char* json_str) 560 | { 561 | // http://www.jsonrpc.org/specification 562 | // code message meaning 563 | // -32700 Parse error Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text. 564 | // -32600 Invalid Request The JSON sent is not a valid Request object. 565 | // -32601 Method not found The method does not exist / is not available. 566 | // -32602 Invalid params Invalid method parameter(s). 567 | // -32603 Internal error Internal JSON-RPC error. 568 | // -32000 to -32099 Server error Reserved for implementation-defined server-errors. 569 | try 570 | { 571 | parse_json(Json::parse(json_str)); 572 | } 573 | catch (const RpcException&) 574 | { 575 | throw; 576 | } 577 | catch (const std::exception& e) 578 | { 579 | throw ParseErrorException(e.what()); 580 | } 581 | } 582 | 583 | inline void Entity::parse(const std::string& json_str) 584 | { 585 | parse(json_str.c_str()); 586 | } 587 | 588 | inline std::string Entity::type_str() const 589 | { 590 | switch (entity) 591 | { 592 | case entity_t::unknown: 593 | return "unknown"; 594 | case entity_t::id: 595 | return "id"; 596 | case entity_t::exception: 597 | return "exception"; 598 | case entity_t::error: 599 | return "error"; 600 | case entity_t::response: 601 | return "response"; 602 | case entity_t::request: 603 | return "request"; 604 | case entity_t::notification: 605 | return "notification"; 606 | case entity_t::batch: 607 | return "batch"; 608 | default: 609 | return "unknown"; 610 | } 611 | } 612 | 613 | 614 | /////////////////////////// NullableEntity implementation ///////////////////// 615 | 616 | inline NullableEntity::NullableEntity(entity_t type) : Entity(type), isNull(false) 617 | { 618 | } 619 | 620 | inline NullableEntity::NullableEntity(entity_t type, std::nullptr_t) : Entity(type), isNull(true) 621 | { 622 | } 623 | 624 | 625 | /////////////////////////// Id implementation ///////////////////////////////// 626 | 627 | inline Id::Id() : Entity(entity_t::id), type_(value_t::null), int_id_(0), string_id_("") 628 | { 629 | } 630 | 631 | inline Id::Id(int id) : Entity(entity_t::id), type_(value_t::integer), int_id_(id), string_id_("") 632 | { 633 | } 634 | 635 | inline Id::Id(const char* id) : Entity(entity_t::id), type_(value_t::string), int_id_(0), string_id_(id) 636 | { 637 | } 638 | 639 | inline Id::Id(const std::string& id) : Id(id.c_str()) 640 | { 641 | } 642 | 643 | inline Id::Id(const Json& json_id) : Id() 644 | { 645 | Id::parse_json(json_id); 646 | } 647 | 648 | inline void Id::parse_json(const Json& json) 649 | { 650 | if (json.is_null()) 651 | { 652 | type_ = value_t::null; 653 | } 654 | else if (json.is_number_integer()) 655 | { 656 | int_id_ = json.get(); 657 | type_ = value_t::integer; 658 | } 659 | else if (json.is_string()) 660 | { 661 | string_id_ = json.get(); 662 | type_ = value_t::string; 663 | } 664 | else 665 | throw std::invalid_argument("id must be integer, string or null"); 666 | } 667 | 668 | inline Json Id::to_json() const 669 | { 670 | if (type_ == value_t::null) 671 | return nullptr; 672 | if (type_ == value_t::string) 673 | return string_id_; 674 | if (type_ == value_t::integer) 675 | return int_id_; 676 | 677 | return nullptr; 678 | } 679 | 680 | 681 | //////////////////////// Error implementation ///////////////////////////////// 682 | 683 | inline Parameter::Parameter(std::nullptr_t) : NullableEntity(entity_t::id, nullptr), type(value_t::null) 684 | { 685 | } 686 | 687 | inline Parameter::Parameter(const Json& json) : NullableEntity(entity_t::id), type(value_t::null) 688 | { 689 | if (json != nullptr) 690 | Parameter::parse_json(json); 691 | } 692 | 693 | inline Parameter::Parameter(const std::string& key1, const Json& value1, const std::string& key2, const Json& value2, const std::string& key3, 694 | const Json& value3, const std::string& key4, const Json& value4) 695 | : NullableEntity(entity_t::id), type(value_t::map) 696 | { 697 | param_map[key1] = value1; 698 | if (!key2.empty()) 699 | add(key2, value2); 700 | if (!key3.empty()) 701 | add(key3, value3); 702 | if (!key4.empty()) 703 | add(key4, value4); 704 | } 705 | 706 | inline void Parameter::add(const std::string& key, const Json& value) 707 | { 708 | param_map[key] = value; 709 | } 710 | 711 | inline void Parameter::parse_json(const Json& json) 712 | { 713 | if (json.is_null()) 714 | { 715 | param_array.clear(); 716 | param_map.clear(); 717 | type = value_t::null; 718 | isNull = true; 719 | } 720 | else if (json.is_array()) 721 | { 722 | param_array = json.get>(); 723 | param_map.clear(); 724 | type = value_t::array; 725 | } 726 | else 727 | { 728 | param_map = json.get>(); 729 | param_array.clear(); 730 | type = value_t::map; 731 | } 732 | } 733 | 734 | inline Json Parameter::to_json() const 735 | { 736 | if (type == value_t::array) 737 | return param_array; 738 | if (type == value_t::map) 739 | return param_map; 740 | 741 | return nullptr; 742 | } 743 | 744 | inline bool Parameter::is_array() const 745 | { 746 | return type == value_t::array; 747 | } 748 | 749 | inline bool Parameter::is_map() const 750 | { 751 | return type == value_t::map; 752 | } 753 | 754 | inline bool Parameter::is_null() const 755 | { 756 | return type == value_t::null; 757 | } 758 | 759 | inline bool Parameter::has(const std::string& key) const 760 | { 761 | if (type != value_t::map) 762 | return false; 763 | return (param_map.find(key) != param_map.end()); 764 | } 765 | 766 | inline Json Parameter::get(const std::string& key) const 767 | { 768 | return param_map.at(key); 769 | } 770 | 771 | inline bool Parameter::has(size_t idx) const 772 | { 773 | if (type != value_t::array) 774 | return false; 775 | return (param_array.size() > idx); 776 | } 777 | 778 | inline Json Parameter::get(size_t idx) const 779 | { 780 | return param_array.at(idx); 781 | } 782 | 783 | 784 | //////////////////////// Error implementation ///////////////////////////////// 785 | 786 | inline Error::Error(const Json& json) : Error("Internal error", -32603, nullptr) 787 | { 788 | if (json != nullptr) 789 | Error::parse_json(json); 790 | } 791 | 792 | inline Error::Error(std::nullptr_t) : NullableEntity(entity_t::error, nullptr), code_(0), message_(""), data_(nullptr) 793 | { 794 | } 795 | 796 | inline Error::Error(const std::string& message, int code, const Json& data) : NullableEntity(entity_t::error), code_(code), message_(message), data_(data) 797 | { 798 | } 799 | 800 | inline void Error::parse_json(const Json& json) 801 | { 802 | try 803 | { 804 | if (json.count("code") == 0) 805 | throw RpcException("code is missing"); 806 | code_ = json["code"]; 807 | if (json.count("message") == 0) 808 | throw RpcException("message is missing"); 809 | message_ = json["message"].get(); 810 | if (json.count("data") != 0u) 811 | data_ = json["data"]; 812 | else 813 | data_ = nullptr; 814 | } 815 | catch (const RpcException&) 816 | { 817 | throw; 818 | } 819 | catch (const std::exception& e) 820 | { 821 | throw RpcException(e.what()); 822 | } 823 | } 824 | 825 | inline Json Error::to_json() const 826 | { 827 | Json j = {{"code", code_}, {"message", message_}}; 828 | 829 | if (!data_.is_null()) 830 | j["data"] = data_; 831 | return j; 832 | } 833 | 834 | 835 | ////////////////////// Request implementation ///////////////////////////////// 836 | 837 | inline Request::Request(const Json& json) : Entity(entity_t::request), method_(""), id_() 838 | { 839 | if (json != nullptr) 840 | Request::parse_json(json); 841 | } 842 | 843 | inline Request::Request(const Id& id, const std::string& method, const Parameter& params) : Entity(entity_t::request), method_(method), params_(params), id_(id) 844 | { 845 | } 846 | 847 | inline void Request::parse_json(const Json& json) 848 | { 849 | try 850 | { 851 | if (json.count("id") == 0) 852 | throw InvalidRequestException("id is missing"); 853 | 854 | try 855 | { 856 | id_ = Id(json["id"]); 857 | } 858 | catch (const std::exception& e) 859 | { 860 | throw InvalidRequestException(e.what()); 861 | } 862 | 863 | if (json.count("jsonrpc") == 0) 864 | throw InvalidRequestException("jsonrpc is missing", id_); 865 | std::string jsonrpc = json["jsonrpc"].get(); 866 | if (jsonrpc != "2.0") 867 | throw InvalidRequestException("invalid jsonrpc value: " + jsonrpc, id_); 868 | 869 | if (json.count("method") == 0) 870 | throw InvalidRequestException("method is missing", id_); 871 | if (!json["method"].is_string()) 872 | throw InvalidRequestException("method must be a string value", id_); 873 | method_ = json["method"].get(); 874 | if (method_.empty()) 875 | throw InvalidRequestException("method must not be empty", id_); 876 | 877 | if (json.count("params") != 0u) 878 | params_.parse_json(json["params"]); 879 | else 880 | params_ = nullptr; 881 | } 882 | catch (const RequestException&) 883 | { 884 | throw; 885 | } 886 | catch (const std::exception& e) 887 | { 888 | throw InternalErrorException(e.what(), id_); 889 | } 890 | } 891 | 892 | inline Json Request::to_json() const 893 | { 894 | Json json = {{"jsonrpc", "2.0"}, {"method", method_}, {"id", id_.to_json()}}; 895 | 896 | if (params_) 897 | json["params"] = params_.to_json(); 898 | 899 | return json; 900 | } 901 | 902 | 903 | inline RpcException::RpcException(const char* text) : m_(text) 904 | { 905 | } 906 | 907 | inline RpcException::RpcException(const std::string& text) : RpcException(text.c_str()) 908 | { 909 | } 910 | 911 | inline const char* RpcException::what() const noexcept 912 | { 913 | return m_.what(); 914 | } 915 | 916 | 917 | inline RpcEntityException::RpcEntityException(const Error& error) : RpcException(error.message()), Entity(entity_t::exception), error_(error) 918 | { 919 | } 920 | 921 | inline void RpcEntityException::parse_json(const Json& /*json*/) 922 | { 923 | } 924 | 925 | 926 | inline ParseErrorException::ParseErrorException(const Error& error) : RpcEntityException(error) 927 | { 928 | } 929 | 930 | inline ParseErrorException::ParseErrorException(const std::string& data) : ParseErrorException(Error("Parse error", -32700, data)) 931 | { 932 | } 933 | 934 | inline Json ParseErrorException::to_json() const 935 | { 936 | Json response = {{"jsonrpc", "2.0"}, {"error", error_.to_json()}, {"id", nullptr}}; 937 | 938 | return response; 939 | } 940 | 941 | 942 | inline RequestException::RequestException(const Error& error, const Id& requestId) : RpcEntityException(error), id_(requestId) 943 | { 944 | } 945 | 946 | inline Json RequestException::to_json() const 947 | { 948 | Json response = {{"jsonrpc", "2.0"}, {"error", error_.to_json()}, {"id", id_.to_json()}}; 949 | 950 | return response; 951 | } 952 | 953 | 954 | inline InvalidRequestException::InvalidRequestException(const Id& requestId) : RequestException(Error("Invalid request", -32600), requestId) 955 | { 956 | } 957 | 958 | inline InvalidRequestException::InvalidRequestException(const Request& request) : InvalidRequestException(request.id()) 959 | { 960 | } 961 | 962 | inline InvalidRequestException::InvalidRequestException(const char* data, const Id& requestId) 963 | : RequestException(Error("Invalid request", -32600, data), requestId) 964 | { 965 | } 966 | 967 | inline InvalidRequestException::InvalidRequestException(const std::string& data, const Id& requestId) : InvalidRequestException(data.c_str(), requestId) 968 | { 969 | } 970 | 971 | 972 | inline MethodNotFoundException::MethodNotFoundException(const Id& requestId) : RequestException(Error("Method not found", -32601), requestId) 973 | { 974 | } 975 | 976 | inline MethodNotFoundException::MethodNotFoundException(const Request& request) : MethodNotFoundException(request.id()) 977 | { 978 | } 979 | 980 | inline MethodNotFoundException::MethodNotFoundException(const char* data, const Id& requestId) 981 | : RequestException(Error("Method not found", -32601, data), requestId) 982 | { 983 | } 984 | 985 | inline MethodNotFoundException::MethodNotFoundException(const std::string& data, const Id& requestId) : MethodNotFoundException(data.c_str(), requestId) 986 | { 987 | } 988 | 989 | 990 | inline InvalidParamsException::InvalidParamsException(const Id& requestId) : RequestException(Error("Invalid params", -32602), requestId) 991 | { 992 | } 993 | 994 | inline InvalidParamsException::InvalidParamsException(const Request& request) : InvalidParamsException(request.id()) 995 | { 996 | } 997 | 998 | inline InvalidParamsException::InvalidParamsException(const char* data, const Id& requestId) 999 | : RequestException(Error("Invalid params", -32602, data), requestId) 1000 | { 1001 | } 1002 | 1003 | inline InvalidParamsException::InvalidParamsException(const std::string& data, const Id& requestId) : InvalidParamsException(data.c_str(), requestId) 1004 | { 1005 | } 1006 | 1007 | 1008 | inline InternalErrorException::InternalErrorException(const Id& requestId) : RequestException(Error("Internal error", -32603), requestId) 1009 | { 1010 | } 1011 | 1012 | inline InternalErrorException::InternalErrorException(const Request& request) : InternalErrorException(request.id()) 1013 | { 1014 | } 1015 | 1016 | inline InternalErrorException::InternalErrorException(const char* data, const Id& requestId) 1017 | : RequestException(Error("Internal error", -32603, data), requestId) 1018 | { 1019 | } 1020 | 1021 | inline InternalErrorException::InternalErrorException(const std::string& data, const Id& requestId) : InternalErrorException(data.c_str(), requestId) 1022 | { 1023 | } 1024 | 1025 | 1026 | ///////////////////// Response implementation ///////////////////////////////// 1027 | 1028 | inline Response::Response(const Json& json) : Entity(entity_t::response) 1029 | { 1030 | if (json != nullptr) 1031 | Response::parse_json(json); 1032 | } 1033 | 1034 | inline Response::Response(const Id& id, const Json& result) : Entity(entity_t::response), id_(id), result_(result), error_(nullptr) 1035 | { 1036 | } 1037 | 1038 | inline Response::Response(const Id& id, const Error& error) : Entity(entity_t::response), id_(id), result_(), error_(error) 1039 | { 1040 | } 1041 | 1042 | inline Response::Response(const Request& request, const Json& result) : Response(request.id(), result) 1043 | { 1044 | } 1045 | 1046 | inline Response::Response(const Request& request, const Error& error) : Response(request.id(), error) 1047 | { 1048 | } 1049 | 1050 | inline Response::Response(const RequestException& exception) : Response(exception.id(), exception.error()) 1051 | { 1052 | } 1053 | 1054 | inline void Response::parse_json(const Json& json) 1055 | { 1056 | try 1057 | { 1058 | error_ = nullptr; 1059 | result_ = nullptr; 1060 | if (json.count("jsonrpc") == 0) 1061 | throw RpcException("jsonrpc is missing"); 1062 | std::string jsonrpc = json["jsonrpc"].get(); 1063 | if (jsonrpc != "2.0") 1064 | throw RpcException("invalid jsonrpc value: " + jsonrpc); 1065 | if (json.count("id") == 0) 1066 | throw RpcException("id is missing"); 1067 | id_ = Id(json["id"]); 1068 | if (json.count("result") != 0u) 1069 | result_ = json["result"]; 1070 | else if (json.count("error") != 0u) 1071 | error_ = json["error"]; 1072 | else 1073 | throw RpcException("response must contain result or error"); 1074 | } 1075 | catch (const RpcException&) 1076 | { 1077 | throw; 1078 | } 1079 | catch (const std::exception& e) 1080 | { 1081 | throw RpcException(e.what()); 1082 | } 1083 | } 1084 | 1085 | inline Json Response::to_json() const 1086 | { 1087 | Json j = {{"jsonrpc", "2.0"}, {"id", id_.to_json()}}; 1088 | 1089 | if (error_) 1090 | j["error"] = error_.to_json(); 1091 | else 1092 | j["result"] = result_; 1093 | 1094 | return j; 1095 | } 1096 | 1097 | 1098 | ///////////////// Notification implementation ///////////////////////////////// 1099 | 1100 | inline Notification::Notification(const Json& json) : Entity(entity_t::notification) 1101 | { 1102 | if (json != nullptr) 1103 | Notification::parse_json(json); 1104 | } 1105 | 1106 | inline Notification::Notification(const char* method, const Parameter& params) : Entity(entity_t::notification), method_(method), params_(params) 1107 | { 1108 | } 1109 | 1110 | inline Notification::Notification(const std::string& method, const Parameter& params) : Notification(method.c_str(), params) 1111 | { 1112 | } 1113 | 1114 | inline void Notification::parse_json(const Json& json) 1115 | { 1116 | try 1117 | { 1118 | if (json.count("jsonrpc") == 0) 1119 | throw RpcException("jsonrpc is missing"); 1120 | std::string jsonrpc = json["jsonrpc"].get(); 1121 | if (jsonrpc != "2.0") 1122 | throw RpcException("invalid jsonrpc value: " + jsonrpc); 1123 | 1124 | if (json.count("method") == 0) 1125 | throw RpcException("method is missing"); 1126 | if (!json["method"].is_string()) 1127 | throw RpcException("method must be a string value"); 1128 | method_ = json["method"].get(); 1129 | if (method_.empty()) 1130 | throw RpcException("method must not be empty"); 1131 | 1132 | if (json.count("params") != 0u) 1133 | params_.parse_json(json["params"]); 1134 | else 1135 | params_ = nullptr; 1136 | } 1137 | catch (const RpcException&) 1138 | { 1139 | throw; 1140 | } 1141 | catch (const std::exception& e) 1142 | { 1143 | throw RpcException(e.what()); 1144 | } 1145 | } 1146 | 1147 | inline Json Notification::to_json() const 1148 | { 1149 | Json json = {{"jsonrpc", "2.0"}, {"method", method_}}; 1150 | 1151 | if (params_) 1152 | json["params"] = params_.to_json(); 1153 | 1154 | return json; 1155 | } 1156 | 1157 | 1158 | //////////////////////// Batch implementation ///////////////////////////////// 1159 | 1160 | inline Batch::Batch(const Json& json) : Entity(entity_t::batch) 1161 | { 1162 | if (json != nullptr) 1163 | Batch::parse_json(json); 1164 | } 1165 | 1166 | inline void Batch::parse_json(const Json& json) 1167 | { 1168 | // cout << "Batch::parse: " << json.dump() << "\n"; 1169 | entities.clear(); 1170 | for (const auto& it : json) 1171 | { 1172 | // cout << "x: " << it->dump() << "\n"; 1173 | entity_ptr entity(nullptr); 1174 | try 1175 | { 1176 | entity = Parser::do_parse_json(it); 1177 | if (!entity) 1178 | entity = std::make_shared("Invalid Request", -32600); 1179 | } 1180 | catch (const RequestException& e) 1181 | { 1182 | entity = std::make_shared(e); 1183 | } 1184 | catch (const std::exception& e) 1185 | { 1186 | entity = std::make_shared(e.what(), -32600); 1187 | } 1188 | entities.push_back(entity); 1189 | } 1190 | if (entities.empty()) 1191 | throw InvalidRequestException(); 1192 | } 1193 | 1194 | inline Json Batch::to_json() const 1195 | { 1196 | Json result; 1197 | for (const auto& j : entities) 1198 | result.push_back(j->to_json()); 1199 | return result; 1200 | } 1201 | 1202 | 1203 | //////////////////////// Parser implementation //////////////////////////////// 1204 | 1205 | inline void Parser::register_notification_callback(const std::string& notification, notification_callback callback) 1206 | { 1207 | if (callback) 1208 | notification_callbacks_[notification] = callback; 1209 | } 1210 | 1211 | inline void Parser::register_request_callback(const std::string& request, request_callback callback) 1212 | { 1213 | if (callback) 1214 | request_callbacks_[request] = callback; 1215 | } 1216 | 1217 | inline entity_ptr Parser::parse(const std::string& json_str) 1218 | { 1219 | // std::cout << "parse: " << json_str << "\n"; 1220 | entity_ptr entity = do_parse(json_str); 1221 | if (entity && entity->is_notification()) 1222 | { 1223 | notification_ptr notification = std::dynamic_pointer_cast(entity); 1224 | if (notification_callbacks_.find(notification->method()) != notification_callbacks_.end()) 1225 | { 1226 | notification_callback callback = notification_callbacks_[notification->method()]; 1227 | if (callback) 1228 | callback(notification->params()); 1229 | } 1230 | } 1231 | else if (entity && entity->is_request()) 1232 | { 1233 | request_ptr request = std::dynamic_pointer_cast(entity); 1234 | if (request_callbacks_.find(request->method()) != request_callbacks_.end()) 1235 | { 1236 | request_callback callback = request_callbacks_[request->method()]; 1237 | if (callback) 1238 | { 1239 | jsonrpcpp::response_ptr response = callback(request->id(), request->params()); 1240 | if (response) 1241 | return response; 1242 | } 1243 | } 1244 | } 1245 | return entity; 1246 | } 1247 | 1248 | inline entity_ptr Parser::parse_json(const Json& json) 1249 | { 1250 | return do_parse_json(json); 1251 | } 1252 | 1253 | inline entity_ptr Parser::do_parse(const std::string& json_str) 1254 | { 1255 | try 1256 | { 1257 | return do_parse_json(Json::parse(json_str)); 1258 | } 1259 | catch (const RpcException&) 1260 | { 1261 | throw; 1262 | } 1263 | catch (const std::exception& e) 1264 | { 1265 | throw ParseErrorException(e.what()); 1266 | } 1267 | catch (...) 1268 | { 1269 | return nullptr; 1270 | } 1271 | } 1272 | 1273 | inline entity_ptr Parser::do_parse_json(const Json& json) 1274 | { 1275 | try 1276 | { 1277 | if (is_request(json)) 1278 | return std::make_shared(json); 1279 | if (is_notification(json)) 1280 | return std::make_shared(json); 1281 | if (is_response(json)) 1282 | return std::make_shared(json); 1283 | if (is_batch(json)) 1284 | return std::make_shared(json); 1285 | } 1286 | catch (const RpcException&) 1287 | { 1288 | throw; 1289 | } 1290 | catch (const std::exception& e) 1291 | { 1292 | throw RpcException(e.what()); 1293 | } 1294 | 1295 | return nullptr; 1296 | } 1297 | 1298 | inline bool Parser::is_request(const std::string& json_str) 1299 | { 1300 | try 1301 | { 1302 | return is_request(Json::parse(json_str)); 1303 | } 1304 | catch (const std::exception&) 1305 | { 1306 | return false; 1307 | } 1308 | } 1309 | 1310 | inline bool Parser::is_request(const Json& json) 1311 | { 1312 | return ((json.count("method") != 0u) && (json.count("id") != 0u)); 1313 | } 1314 | 1315 | inline bool Parser::is_notification(const std::string& json_str) 1316 | { 1317 | try 1318 | { 1319 | return is_notification(Json::parse(json_str)); 1320 | } 1321 | catch (const std::exception&) 1322 | { 1323 | return false; 1324 | } 1325 | } 1326 | 1327 | inline bool Parser::is_notification(const Json& json) 1328 | { 1329 | return ((json.count("method") != 0u) && (json.count("id") == 0)); 1330 | } 1331 | 1332 | inline bool Parser::is_response(const std::string& json_str) 1333 | { 1334 | try 1335 | { 1336 | return is_response(Json::parse(json_str)); 1337 | } 1338 | catch (const std::exception&) 1339 | { 1340 | return false; 1341 | } 1342 | } 1343 | 1344 | inline bool Parser::is_response(const Json& json) 1345 | { 1346 | return (((json.count("result") != 0u) || (json.count("error") != 0u)) && (json.count("id") != 0u)); 1347 | } 1348 | 1349 | inline bool Parser::is_batch(const std::string& json_str) 1350 | { 1351 | try 1352 | { 1353 | return is_batch(Json::parse(json_str)); 1354 | } 1355 | catch (const std::exception&) 1356 | { 1357 | return false; 1358 | } 1359 | } 1360 | 1361 | inline bool Parser::is_batch(const Json& json) 1362 | { 1363 | return (json.is_array()); 1364 | } 1365 | 1366 | } // namespace jsonrpcpp 1367 | 1368 | #endif 1369 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | FetchContent_Declare( 4 | Catch2 5 | GIT_REPOSITORY https://github.com/catchorg/Catch2.git 6 | GIT_TAG v3.6.0 # or a later release 7 | ) 8 | 9 | FetchContent_MakeAvailable(Catch2) 10 | 11 | # Make test executable 12 | add_executable(jsonrpcpp_test ${CMAKE_CURRENT_SOURCE_DIR}/test_main.cpp) 13 | 14 | target_link_libraries(jsonrpcpp_test Catch2::Catch2WithMain) 15 | -------------------------------------------------------------------------------- /test/test_main.cpp: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of jsonrpc++ 3 | Copyright (C) 2017-2024 Johannes Pohl 4 | 5 | This software may be modified and distributed under the terms 6 | of the MIT license. See the LICENSE file for details. 7 | ***/ 8 | 9 | // local headers 10 | #include "jsonrpcpp.hpp" 11 | 12 | // 3rd party headers 13 | #include 14 | 15 | 16 | using namespace std; 17 | 18 | TEST_CASE("Main test") 19 | { 20 | jsonrpcpp::entity_ptr entity = 21 | jsonrpcpp::Parser::do_parse(R"({"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3})"); 22 | REQUIRE(entity->is_request()); 23 | jsonrpcpp::request_ptr request = dynamic_pointer_cast(entity); 24 | REQUIRE(request->method() == "subtract"); 25 | int result = request->params().get("minuend") - request->params().get("subtrahend"); 26 | REQUIRE(result == 19); 27 | jsonrpcpp::Response response(*request, result); 28 | REQUIRE(response.id().type() == jsonrpcpp::Id::value_t::integer); 29 | REQUIRE(response.id().int_id() == 3); 30 | REQUIRE(response.result() == 19); 31 | REQUIRE(response.to_json() == nlohmann::json::parse(R"({"jsonrpc": "2.0", "result": 19, "id": 3})")); 32 | } 33 | 34 | TEST_CASE("Null parameter") 35 | { 36 | jsonrpcpp::entity_ptr entity = jsonrpcpp::Parser::do_parse(R"({"jsonrpc": "2.0", "method": "nullrequest", "params": null, "id": 4})"); 37 | REQUIRE(entity->is_request()); 38 | jsonrpcpp::request_ptr request = dynamic_pointer_cast(entity); 39 | REQUIRE(request->method() == "nullrequest"); 40 | REQUIRE(request->params().to_json() == nullptr); 41 | REQUIRE(request->params().is_null() == true); 42 | int result = 12; 43 | jsonrpcpp::Response response(*request, result); 44 | REQUIRE(response.id().type() == jsonrpcpp::Id::value_t::integer); 45 | REQUIRE(response.id().int_id() == 4); 46 | REQUIRE(response.result() == 12); 47 | REQUIRE(response.to_json() == nlohmann::json::parse(R"({"jsonrpc": "2.0", "result": 12, "id": 4})")); 48 | } --------------------------------------------------------------------------------