Skip to content

libdevcore/JSON.h - new API#3532

Merged
axic merged 1 commit intoargotorg:developfrom
aarlt:libdevcore_new_json_api
Feb 20, 2018
Merged

libdevcore/JSON.h - new API#3532
axic merged 1 commit intoargotorg:developfrom
aarlt:libdevcore_new_json_api

Conversation

@aarlt
Copy link
Contributor

@aarlt aarlt commented Feb 15, 2018

  • removed deprecated api to be compatible with newer versions of jsoncpp
  • usage of strict-mode parsing
  • added json-specific tests in test/libdevcore/JSON.cpp
  • added unexpected_trailing_test to test/libsolidity/StandardCompiler.cpp

see issue #3341 & #3355

/// \param _json [out] resulting JSON object
/// \param _errs [out] Formatted error messages
/// \return \c true if the document was successfully parsed, \c false if an error occurred.
inline bool parse(Json::CharReaderBuilder& _builder, const std::string &_input, Json::Value *_json, std::string *_errs)
Copy link
Contributor

@axic axic Feb 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of pointers can we use references?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also thought about this. The good thing with pointers are, that the direction of read/write is more visible - but thats mainly somehow just a matter of taste. Of course this would only make sense, if it's not only used in libdevcore/JSON.h.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our general guideline is to only use a pointer if the pointer can be nullptr and references otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I will just change _json to be a reference.

@axic
Copy link
Contributor

axic commented Feb 15, 2018

Most of the helpers were inline in JSON.h because they were tiny. The new version of the helpers seem to be a bit bigger, but more importantly they are used in more places than before. I think it makes sense moving the implementation out of the header.

I might be wrong about this, any opinions @chriseth @aarlt ?

string compileMulti(string const& _input, bool _optimize, CStyleReadFileCallback _readCallback = nullptr)
{
Json::Reader reader;
std::string errs;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The std namespace is imported in this file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also it could be just errors to avoid abbreviations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I really love explicitly written namespaces - but I will change my coding style for you. But errs should really be renamed to errors.

std::string errs;
Json::Value input;
if (!reader.parse(_input, input, false))
if (!dev::jsonParseStrict(_input, &input, &errs))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dev namespace is imported in this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yop.

Json::Value input;
Json::Reader reader;

std::string errs;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments here as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yop. Yop.

{
Json::Value metadata;
if (!Json::Reader().parse(_metadata, metadata, false))
if (!dev::jsonParseStrict(_metadata, &metadata))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is within the dev namespace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

;)

Json::Value config;
BOOST_REQUIRE(Json::Reader().parse(c_configString, config));
for (auto const& account: _accounts)
BOOST_REQUIRE(dev::jsonParseStrict(c_configString, &config));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, this file imports the dev namespace.


// comments are allowed in strict-mode? - that's strange...
BOOST_CHECK(
dev::jsonParseStrict("{\"1\":3, // awesome comment\n\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":5}}", &json,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In these files please follow the coding style, roughly:

	BOOST_CHECK(dev::jsonParseStrict(
              "{\"1\":3, // awesome comment\n\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":5}}",
              &errors
         ));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thats much better.

"\n but got:\n" << generatedDocumentation.toStyledString()
);
dev::jsonParseStrict(_expectedDocumentationString, &expectedDocumentation);
BOOST_CHECK_MESSAGE(expectedDocumentation == generatedDocumentation,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not change the coding style here.

BOOST_CHECK(containsError(result, "JSONError", "Input is not a JSON object."));
BOOST_CHECK(!containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n"));
BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n A valid JSON document must be either an array or an object value.\n"));
BOOST_CHECK(!containsError(result, "JSONError","* Line 1, Column 1\n Syntax error: value, object or array expected.\n"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also avoid changing the coding style in the lines above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry.

@aarlt
Copy link
Contributor Author

aarlt commented Feb 16, 2018

Most of the helpers were inline in JSON.h because they were tiny. The new version of the helpers seem to be a bit bigger, but more importantly they are used in more places than before. I think it makes sense moving the implementation out of the header.

I might be wrong about this, any opinions @chriseth @aarlt ?

It's definitely on the edge to get moved out of the header. I would like to move it.

{

/// StreamWriterBuilder that can be constructed with specific settings
class StreamWriterBuilder : public Json::StreamWriterBuilder {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't put a space in front of : (except in x ? y : z).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

/// StreamWriterBuilder that can be constructed with specific settings
class StreamWriterBuilder : public Json::StreamWriterBuilder {
public:
explicit StreamWriterBuilder(const std::map<std::string, std::string> &_settings)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put the const after the type

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know const T& is just an alternative notation of T& const. I know that this notation is sometimes preferred, but why exactly?

/// \param _input JSON input string
/// \param _builder StreamWriterBuilder that is used to create new Json::StreamWriter
/// \return serialized json object
inline std::string print(Json::Value const &_input, Json::StreamWriterBuilder &_builder) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put { on its own line and move & to the left: Json::Value const& _input

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

inline std::string jsonPrettyPrint(Json::Value const& _input)
{
return Json::StyledWriter().write(_input);
static std::map<std::string, std::string> settings{{"indentation", " "}};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this local variable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To what local variable you're referring to? The static settings object is used to configure the static StreamWriterBuilder.

};

/// CharReaderBuilder with strict-mode settings
class StrictModeCharReaderBuilder : public Json::CharReaderBuilder {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think those classes are better suited in an anonymous namespace in the .cpp file - they are only used by the functions in this header, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, currently it's just used by the functions in header. I thought that it could make sense to allow others to use those classes. But I think that will probably never used, because we can already parse and print JSON objects, that are the most important operations. I moved it to the .cpp file.

@aarlt aarlt force-pushed the libdevcore_new_json_api branch from b21c97d to b75e3a1 Compare February 16, 2018 17:36
@aarlt
Copy link
Contributor Author

aarlt commented Feb 17, 2018

@axic @chriseth Why is there that error? Is that related to my changes?

     Error: Could not connect to your Ethereum client. Please check that your Ethereum client:
    - is running
    - is accepting RPC connections (i.e., "--rpc" option is used in geth)
    - is accessible over the network
    - is properly configured in your Truffle configuration file (truffle.js)

@chriseth
Copy link
Contributor

I'm running a small cleanup on the coding style and will force-push to your branch, so please do not overwrite later.

@chriseth chriseth force-pushed the libdevcore_new_json_api branch from b75e3a1 to c3bc0c7 Compare February 19, 2018 14:44
@chriseth
Copy link
Contributor

@aarlt the error is unrelated to your change.

@axic can be merged from my side.

/// \param _json [out] resulting JSON object
/// \param _errs [out] Formatted error messages
/// \return \c true if the document was successfully parsed, \c false if an error occurred.
bool jsonParse(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every use cases uses strict mode - do we want to keep this non-strict helper?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question.. will we need to parse non-strict JSON in the future?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, though it's small we can keep it.

@axic
Copy link
Contributor

axic commented Feb 20, 2018

From the strict mode documentation:

   /** \brief A configuration that is strictly compatible with the JSON
   * specification.
   * - Comments are forbidden.
   * - Root object must be either an array or an object value.
   * - Assumes Value strings are encoded in UTF-8
   */
  static Features strictMode();

Perhaps it makes sense adding two more tests: comments and invalid UTF-8 strings.

@aarlt
Copy link
Contributor Author

aarlt commented Feb 20, 2018

@axic I already included a test to check the comments and I was really surprised that they where allowed. Now I just took a look to the jsoncpp code - I really don't know why they write here Comments are forbidden. - if allowComments_ is false, they just skip CommentTokens.

void Reader::skipCommentTokens(Token& token) {
  if (features_.allowComments_) {
    do {
      readToken(token);
    } while (token.type_ == tokenComment);
  } else {
    readToken(token);
  }
}

It looks like that they just disable the collection of comments in there AST. So there will be no error generated. Probably allowComments is just a translation error or something like that, it should be ignoreComments.

Regarding Assumes Value strings are encoded in UTF-8 I think it just means that it assumes that the values are UTF-8 encoded, there is also no real check regarding that. The only thing I saw in the code was a check regarding UTF-8 escape sequences - but that is of course also done with non-strict parsing.

@aarlt
Copy link
Contributor Author

aarlt commented Feb 20, 2018

@chriseth Wow, I didn't knew that a force-push to my branch is possible. Good to know.

@axic
Copy link
Contributor

axic commented Feb 20, 2018

I really don't know why they write here Comments are forbidden. - if allowComments_ is false, they just skip CommentTokens.

We should submit a patch for "super strict", pedantic or "canonical json" mode :)

Regarding Assumes Value strings are encoded in UTF-8 I think it just means that it assumes that the values are UTF-8 encoded, there is also no real check regarding that. The only thing I saw in the code was a check regarding UTF-8 escape sequences - but that is of course also done with non-strict parsing.

Ok - I assumed the parser would fail on an invalid UTF-8 sequence. I'll add such a test case anyway so we'll trigger a failure once/if they implement it.

@axic axic force-pushed the libdevcore_new_json_api branch from c3bc0c7 to c4ed18b Compare February 20, 2018 20:56
@axic axic force-pushed the libdevcore_new_json_api branch from c4ed18b to 0f29ac4 Compare February 20, 2018 20:58
@axic axic merged commit 70790d4 into argotorg:develop Feb 20, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments