Loki-Astari / ThorsMongo

C++ MongoDB API and BSON/JSON Serialization library
GNU General Public License v3.0
316 stars 72 forks source link

Optional and required values #39

Closed ghost closed 5 years ago

ghost commented 5 years ago

Hi,

I would like to use your lib with the following use-case:

How do I get the behavior that

The following code has all 4 examples I consider: Is there a way to make t1, t2 and t3 work and let import into t4 throw an exception?

#include <iostream>

#include <ThorSerialize/JsonThor.h>
#include <ThorSerialize/Traits.h>

using namespace std;
using namespace ThorsAnvil::Serialize;

struct Test {
    int num;
    int* optionalNum;
    ~Test() { delete optionalNum; }
};

ThorsAnvil_MakeTrait(Test, num, optionalNum);

int main()
{
    using PT = ParserInterface::ParseType;
    Test t1, t2, t3, t4;

    // should work perfectly: works
    string twoVals = R"( {"num": 42, "optionalNum": 43} )";
    istringstream(twoVals) >> jsonImport(t1, PT::Weak);
    cout << jsonExport(t1) << endl;

    // ignoring additional values is fine: works
    string threeVals = R"( {"num": 42, "optionalNum": 43, "tooMany": 44} )";
    istringstream(threeVals) >> jsonImport(t2, PT::Weak);
    cout << jsonExport(t2) << endl;

    // non-defined pointer members are nullptr: works
    string oneVal = R"( {"num": 42} )";
    istringstream(oneVal) >> jsonImport(t3, PT::Weak);
    cout << jsonExport(t3) << endl;

    // class T has a member "num" which is missing in the JSON -> exception?
    string zeroVals = "{}";
    istringstream(zeroVals) >> jsonImport(t4, PT::Weak);
    cout << jsonExport(t4) << endl;

    return 0;
}

Thanks

Loki-Astari commented 5 years ago

I would like to use your lib with the following use-case:

Loki-Astari commented 5 years ago

How do I get the behavior that

Loki-Astari commented 5 years ago

Note: Declaring variables like this:

Test t1, t2, t3, t4;

Is probably not a good idea when you have not define the constructor. The members of these objects are uninitialized. It is just random coincidence that that member optionalNum is nullptr when you check.

If you explicitly invoke the zero-initialization then you know that the object is zero-ed before use:

Test t1{}, t2{}, t3{}, t4{};  // Still not good practice to do this on one line.
ghost commented 5 years ago

Thanks for the help. When I have time I will come up with a PR, since we need such a feature. But I cannot promise it.

Loki-Astari commented 5 years ago

I think a goof place to look start is:

 `DeSerializationForBlock::scanObject()`
  ThorsSerializer/src/Serialize/Serialize.tpp  Line 57.

here

To maintain backward compatibility. We should add another type of parser. ParserType::Exact. If the parser type is Exact then we don't allow extra members and we expect exactly all members to be specified. I don't think special casing pointer is a good idea. Pointer members can still be null in the JSON. That way a Exact parser requires all members and no extra members.

To implement this:

Add a std::map<std::string, bool> here scanObject(). Then each time around the loop mark the scanned member as true in your map. Then just before exiting if the parser is Exact then check to see if all members have been parsed. You can find all the members that need to be parsed by calling:

Traits<T>:: getMembers()

This will return a tuple. Each member is a name and a function. You need to go through the tuple members to make sure that each member is in the map you built above. If you can not find a tuple member in the map you built then throw an exception.

Loki-Astari commented 5 years ago

OK. Just pushed a commit that makes this work. I'll submit to brew over the weekend

Loki-Astari commented 5 years ago

The new version is now live with brew.

brew install thors-serializer