jsonrpcx/json-rpc-cxx

Custom JSON Parsers Not Detected

freesurfer-rge opened this issue · 8 comments

I seem to be unable to successfully use methods whose arguments are types such as enum classes, where I have used nlohmann::json to provide serialisation and deserialisation routines. The method successfully binds, but when I invoke it using curl, there is an error response of invalid parameter: must be object, but is string for parameter \"e\"

The attached zip file contains a repro case. The enum itself is:

 enum class MyEnum { Zero, One, Two };

with serialisation provided by NLOHMANN_JSON_SERIALIZE_ENUM. There are then two methods which I'm attempting to call:

  int MyService::OptionOne( MyEnum e ) {
    std::cout << __FUNCTION__ << std::endl;
    return static_cast<int>(e);
  }

  int MyService::OptionTwo( std::string s ) {
    std::cout << __FUNCTION__ << std::endl;
    nlohmann::json j(s);

    MyEnum e = j.get<MyEnum>();

    return static_cast<int>(e);
  }

I can invoke the second via:

curl http://localhost:8483/jsonrpc -H "Content-Type application/json" --data '{ "method":"OptionTwo", "params":{"s":"One"}, "id":10, "jsonrpc": "2.0" }'

However, when I try to call the first:

curl http://localhost:8483/jsonrpc -H "Content-Type application/json" --data '{ "method":"OptionOne", "params":{"e":"One"}, "id":10, "jsonrpc": "2.0" }'

I see the error noted above.

jsonrpccxxissue.zip

Thank you for reporting this. I did not think of this scenario. The typemapper fallsback to object if it does not find a specified typemapping.

Adding the following lines to your code should work:

constexpr nlohmann::json::value_t GetType(jsonrpccxx::type<MyEnum>) {
  return nlohmann::json::value_t::string;
}

I will try to provide a macro for this.

To match your exact example:

#pragma once
#include <nlohmann/json.hpp>
#include <jsonrpccxx/typemapper.hpp>

namespace SimpleService {
  enum class MyEnum { Zero, One, Two };

  NLOHMANN_JSON_SERIALIZE_ENUM(MyEnum,
			       {
				{MyEnum::Zero, "Zero"},
				{MyEnum::One, "One"},
				{MyEnum::Two, "Two"}
			       })

	constexpr nlohmann::json::value_t GetType(jsonrpccxx::type<MyEnum>) {
  		return nlohmann::json::value_t::string;
	}
}

Nevermind my comments above, it should now work out of the box.

Thanks. After also updating the example to pull in the version 3.9.1 of nlohmann's json library, the example code started to work.

I did also find I needed to add a type mapper:

 constexpr nlohmann::json::value_t GetType(jsonrpccxx::type<ItemId>) {
    return nlohmann::json::value_t::string;
  }

into my rpcserver.hpp file (where ItemId is my custom type). Is this expected? I don't see anything like that for Product in the example code.

I could not find anything like a ItemId in your provided example (zip-file). However I assume you have something like

typedef std::string ItemId

in your code, which would require a custom typemapping, yes (At least I cant think of anything how to resolve this automatically).

If you take a look at typemapper.h these are the types that are automatically resolved, all other types fallback to value_t::object.

Sorry, I didn't explain myself well. The attached is an update which shows the problem.

jsonrpccxxissue.zip

The appropriate CURL command is:

curl http://localhost:8483/jsonrpc -H "Content-Type application/json" --data '{ "method":"EchoId", "params":{"id":"00:00:00:10"}, "id":10, "jsonrpc": "2.0" }'

To fix the error, enable the #if 0 block in rpcserver.hpp.

Basically the ItemId type is a wrapper for an unsigned 32 bit number, which takes care of pretty-printing it to a string, and parsing it back afterwards.

In this case it is definitely required. How else should the json deserializer know how to decode the custom class?

Thanks for letting me know.