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

Behavioral change of serializers in 3.11.3. Name lookup related. #4320

Open
2 tasks
TobiSchluter opened this issue Mar 22, 2024 · 1 comment
Open
2 tasks

Comments

@TobiSchluter
Copy link

TobiSchluter commented Mar 22, 2024

Description

This is a behavioral change in nlohmann::json 3.11.3 compared to previous versions. Old code of mine started behaving differently.

Output of the test program changed from:
nlohmann::json 3.11.2 (and older versions)
{"x":1.0,"y":2.0,"z":3.0}
{"x":1.0,"y":2.0,"z":3.0}

To:
nlohmann::json 3.11.3
[1.0,2.0,3.0]
{"x":1.0,"y":2.0,"z":3.0}

The issue appears to be related to the lookup of to_json converters. Notice that the old behavior can be triggered by adding the namespace prefix to the to_json call in N::to_json.

Reproduction steps

Run the code reproduced below. Results will differ between 3.11.2 and 3.11.3.

It needs the Eigen library, version 3.4.0. What appears to happen is that the custom vector type is recognized as "array compatible" and then output as such.

Expected vs. actual results

The output should be as in older versions of nlohmann::json:
{"x":1.0,"y":2.0,"z":3.0}
{"x":1.0,"y":2.0,"z":3.0}
instead we get
[1.0,2.0,3.0]
{"x":1.0,"y":2.0,"z":3.0}
i.e. my serializer is bypassed.

Minimal code example

#include <Eigen/Dense>
#include <iostream>
#include <nlohmann/json.hpp>

// Types for data exchange.
namespace N {
    struct Vector {
        double x, y, z;
    };
}

namespace L {
    using Vec3d = Eigen::Vector3d;
}

namespace N {
    // I don't recall why my code goes through this intermediate type, but this is likely related to the change in behavior.
    class Vec3d : public L::Vec3d {
    public:
        explicit Vec3d(const L::Vec3d& v) noexcept
            : L::Vec3d(v)
        {}
        Vec3d(const Vector& v) noexcept
            : L::Vec3d(v.x, v.y, v.z)
        {}
        using L::Vec3d::Vec3d;
    };
    inline Vec3d vToEigen(const Vector& v) noexcept { return Vec3d(v); }
}

namespace Eigen {
    inline void to_json(nlohmann::json& j, const L::Vec3d& v) noexcept
    {
        j = { { "x", v.x() },{ "y", v.y() },{ "z", v.z() } };
    }
}

namespace N {
    // Lookup of to_json changed.  Previously this function behaved the same as ...
    inline void to_json(nlohmann::json& j, const Vector& v) noexcept {
        to_json(j, vToEigen(v));
    }
    // ... this function
    inline void to_json_force(nlohmann::json& j, const Vector& v) noexcept {
        Eigen::to_json(j, vToEigen(v));
    }
}

int main()
{
    N::Vector vec{ 1.0, 2.0, 3.0 };

    // Both outputs were the same until and including nlohmann::json 3.11.2
    nlohmann::json json;
    N::to_json(json, vec);
    std::cout << json.dump() << "\n";

    nlohmann::json json_forced;
    N::to_json_force(json_forced, vec);
    std::cout << json_forced.dump() << "\n";
}

Error messages

No response

Compiler and operating system

msvc 2022 (latest) on Windows x86-64 and gcc 13 on Linux-x64

Library version

3.11.3

Validation

@TobiSchluter TobiSchluter changed the title Behavioral change in 3.11.3 Behavioral change of serializers in 3.11.3. Name lookup related. Mar 22, 2024
@TobiSchluter
Copy link
Author

This also happens with the trunk version at godbolt: https://godbolt.org/z/3neMTxvYc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant