Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

loading from a stream and exceptions #582

Closed
user1095108 opened this issue May 13, 2017 · 35 comments
Closed

loading from a stream and exceptions #582

user1095108 opened this issue May 13, 2017 · 35 comments
Labels
kind: question solution: proposed fix a fix for the issue has been proposed and waits for confirmation state: please discuss please discuss the issue or vote for your favorite option

Comments

@user1095108
Copy link

user1095108 commented May 13, 2017

There are 2 possible scenarios when loading from a stream:

  • there might be an error while loading from the stream (bad sector),
  • the stream might contain nothing (zero size file),
  • the json the stream contains might be invalid.

I suppose the library will throw meaningful exceptions if those are enabled, but if disabled, it will abort. There should exist an alternative way to report an error (such as returning an empty json object, for example), without crashing an application.

@nlohmann
Copy link
Owner

The status quo is like this:

#include "json.hpp"
#include <fstream>

using json = nlohmann::json;

int main()
{
    std::fstream f_non("nonexisting.json");
    std::fstream f_empty("empty.json");
    std::fstream f_error("error.json");
    
    try {
        json::parse(f_non);
    } catch (json::parse_error &e)
    {
        std::cerr << e.what() << std::endl;
    }

    try {
        json::parse(f_empty);
    } catch (json::parse_error &e)
    {
        std::cerr << e.what() << std::endl;
    }

    try {
        json::parse(f_error);
    } catch (json::parse_error &e)
    {
        std::cerr << e.what() << std::endl;
    }
}

Output:

[json.exception.parse_error.111] parse error: bad input stream
[json.exception.parse_error.101] parse error at 1: syntax error - unexpected end of input
[json.exception.parse_error.101] parse error at 1: syntax error - invalid literal; last read '@'

That is, in all three cases (nonexisting file, empty file, file with syntax error), a json::parse_error exception is thrown. In case exceptions are switched off, all three cases call std::abort().

@user1095108
Copy link
Author

Yes, but the application programmer has no way to notify the user of an error, if you abort. This is something I'd like to be able to do in my app.

@user1095108
Copy link
Author

You could return an empty object or even use std::optional.

@user1095108
Copy link
Author

Or std::pair containing an object and bool. The bool indicating an error.

@nlohmann
Copy link
Owner

A validity check will be implemented with #458. This would avoid the last 2 exceptions. The first exception is thrown in the input_adapter class - I have to check whether it makes sense to remove it there. This could avoid refactoring the parser methods.

Note that std::optional is not part of C++11, and stepping forward to C++17 would exclude too many users.

@nlohmann nlohmann added the state: please discuss please discuss the issue or vote for your favorite option label May 13, 2017
@user1095108
Copy link
Author

You still have std::pair then

@user1095108
Copy link
Author

Also, hacking your own std::optional is not too difficult

@nlohmann
Copy link
Owner

I'm not convinced yet whether changing the signature of the parse function is worth it in the first place.

@user1095108
Copy link
Author

Of course, you should introduce another function. But really, what bothers me most is, if I "pipe" a stream into a Json object your library might abort or throw. This is completely wrong. You should check if exceptions are enabled in the stream before throwing or aborting. You can set flags in the stream, you don't need to throw or abort.

@nlohmann
Copy link
Owner

I understand.

@JakeShirley
Copy link

I too would appreciate being able to parse without using exceptions. 👍

@nlohmann
Copy link
Owner

Opened #623 as place to discuss the structure of the parsing functions.

nlohmann added a commit that referenced this issue Jul 27, 2017
You can now pass a boolean "allow_exceptions" to the parse functions. If it is false, no exceptions are thrown in case of a parse error. Instead, parsing is stopped at the first error and a JSON value of type "discarded" (check with is_discarded()) is returned.
@nlohmann nlohmann added solution: proposed fix a fix for the issue has been proposed and waits for confirmation and removed state: please discuss please discuss the issue or vote for your favorite option labels Jul 27, 2017
@nlohmann
Copy link
Owner

You can now pass a boolean allow_exceptions to the parse functions. If it is false, no exceptions are thrown in case of a parse error. Instead, parsing is stopped at the first error and a JSON value of type "discarded" (check with is_discarded()) is returned.

@user1095108
Copy link
Author

user1095108 commented Jul 27, 2017 via email

@nlohmann
Copy link
Owner

Piping does not work, because you cannot provide an additional parameter. You need to use the parse function:

json j = json::parse(stream, nullptr, false);

(The nullptr means "no callback function" and false means "no exceptions").

((But I could switch off exceptions automatically when JSON_NOEXCEPTION is defined.))

@user1095108
Copy link
Author

user1095108 commented Jul 27, 2017 via email

@nlohmann
Copy link
Owner

Such a manipulator would be syntactic sugar and I am not sure yet whether it would justify the effort.

@user1095108
Copy link
Author

user1095108 commented Jul 27, 2017 via email

@nlohmann
Copy link
Owner

I know - but in the end, this is a lot of work for a feature that (probably) hard anyone uses.

@user1095108
Copy link
Author

user1095108 commented Jul 27, 2017 via email

@nlohmann
Copy link
Owner

Yes. But the parse function is much more versatile. For instance, the >> operator does not support a callback function. Even if I would implement all this, write proper tests and documentation, I think that it would mean a lot of development and maintenance work.

@JakeShirley
Copy link

@nlohmann excellent work! I greatly appreciate your dedication to this library :)

@user1095108
Copy link
Author

It's been so long, that I forgot to remind you, that there is a flag within the stream, that tells you if you don't want it to throw exceptions:

http://en.cppreference.com/w/cpp/io/basic_ios/exceptions

you can simply check that and act accordingly, no need for io manipulators.

@nlohmann
Copy link
Owner

All streams have goodbit by default (they do not throw exceptions due to error state flags being set).

If I would use these flags, then I would change the behavior of the stream operators, because they would not throw unless explicitly specified.

@user1095108
Copy link
Author

user1095108 commented Jul 30, 2017 via email

@nlohmann
Copy link
Owner

Yes. But if I would query the exception flag (which are not set by default) and only throw exceptions on parse errors if the flags are set, then existing code would behave differently. I think this would be an issue.

@user1095108
Copy link
Author

user1095108 commented Jul 30, 2017 via email

@user1095108
Copy link
Author

user1095108 commented Jul 30, 2017 via email

@nlohmann nlohmann added the state: please discuss please discuss the issue or vote for your favorite option label Jul 30, 2017
@nlohmann
Copy link
Owner

I unfortunately do not have any statistics about which functions are used how frequently. And I fear that "fixing" this behavior yields a lot of support questions.

@user1095108
Copy link
Author

user1095108 commented Jul 30, 2017 via email

@user1095108
Copy link
Author

user1095108 commented Jul 30, 2017 via email

@nlohmann
Copy link
Owner

I understand your point. But please also understand that changing the behavior of existing code is also an issue. I really would like to have a broader discussion here.

@user1095108
Copy link
Author

user1095108 commented Jul 30, 2017 via email

@nlohmann
Copy link
Owner

I close this issue without change. Breaking existing code is not an option.

@user1095108
Copy link
Author

I can only say to this: to hell with your library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: question solution: proposed fix a fix for the issue has been proposed and waits for confirmation state: please discuss please discuss the issue or vote for your favorite option
Projects
None yet
Development

No branches or pull requests

3 participants