Skip to content

Please post example of specialization for boost::filesystem #692

Closed

Description

@solvingj

You gave an example for boost::optional which was very nice, however i spent some time trying to adapt with no success, here was my best guess.

  template<>
  struct adl_serializer<boost::filesystem::path>
  {
    static void to_json(json& j, const boost::filesystem::path& path)
    {
      j = path.string();
    }

    static void from_json(const json& j, boost::filesystem::path& path)
    {
      path = boost::filesystem::path(j);
    }
  };
  

Can you please advise?

Activity

  1. nlohmann commented on Aug 11, 2017

    @nlohmann
    Owner

    Did you put the struct into the nlohmann namespace?

  2. nlohmann commented on Aug 11, 2017

    @nlohmann
    Owner

    I realized you pass the JSON value j to boost::filesystem::path in you from_json function.

    This may work:

    #include "src/json.hpp"
    #include <boost/filesystem.hpp>
    
    namespace nlohmann {
      template<>
      struct adl_serializer<boost::filesystem::path>
      {
        static void to_json(json& j, const boost::filesystem::path& path)
        {
          j = path.string();
        }
    
        static void from_json(const json& j, boost::filesystem::path& path)
        {
          path = j.get<std::string>();
        }
      };
    }
    
    int main()
    {
      ...
    }
  3. solvingj commented on Aug 11, 2017

    @solvingj
    Author

    Thanks for the response!
    This produces:
    explicit specialization; 'nlohmann::adl_serializer<T,void>' has already been instantiated
    explicit specialization; 'nlohmann::adl_serializer<T,void>' has already been defined

  4. theodelrieu commented on Aug 11, 2017

    @theodelrieu
    Contributor

    Hello,

    This means you specialized the adl_serializer after one piece of code using filesystem::path altogether with nlohmann::json.

    You should specialize adl_serializer inside a header that only contains such specializations:

    // JsonSpecializations.hpp
    
    #include <json/json.hpp>
    
    namespace nlohmann {
    template<>
    struct adl_serializer<boost::filesystem::path> {
    /* ... */
    };
    }

    In your case, the library already instantiated the default adl_serializer with boost::filesystem::path.
    The reason why you don't see the usual static_assert trigger is due to complex template machinery.

    I think that if you comment out your specialization, you will see a message saying:

    could not find method to_json for type T

  5. solvingj commented on Aug 11, 2017

    @solvingj
    Author

    Ok, if I comment out the section as you did, I get the same error about already instantiated/defined.

    Also, I had a separate file for this specialization of a third-party library, however I did also have to_json and from_json methods in my own classes, and those methods exist alongside their respective class files.

    So, there is indeed a slightly non-basic interaction in my case, because my custom classes have filesystem::path members. So, when I convert my classes to JSON, their to_json methods try to convert filesystem::path members to json, which implicitly should call the to_json from the specialization above.

    Below is one of those classes. Interestingly, I just discovered that when I added the ADL_Specialization, the specialization logic, the below fails to compile now.

    The question is, how does one organize specializations of both custom classes and third-party classes when the former depends on the latter. Do I have to aggregate all my custom class specialization methods into one big header along with the third-party ones?

    #ifndef GLOBAL_CONFIG_HPP_
    #define GLOBAL_CONFIG_HPP_
    
    #include <string>
    #include <boost/filesystem.hpp>
    #include <json.hpp>
    
    using nlohmann::json;
    
    namespace daemon
    {
      namespace config
      {
        namespace bfs = boost::filesystem;
        struct global_config
        {
          std::string log_level;
          bfs::path log_path;
          bfs::path config_path;
        };
    
        void to_json(json& j, const global_config& gc)
        {
          j = json
          {
            { "log_level", gc.log_level },
            { "log_path", gc.log_path },
            { "config_path", gc.config_path }
          };
        }
      }
    }
  6. theodelrieu commented on Aug 11, 2017

    @theodelrieu
    Contributor

    Oh, I meant you needed to comment the whole specialization to see the static_assert, not the part I did comment in the example code, sorry about that.

    I don't know how many third party classes you need to specialize adl_serializer for, with only two or three, you could put them in a single header (e.g json_boost.hpp).

    As for your custom types that use third party types, you MUST include the header json_boost.hpp before the definitions of to_json/from_json.

    Finally, if you have to specialize adl_serializer for some of your types (e.g. a template custom container), it's up to you whether to put them in their own headers or in one big. Your types that provide ADL from_json/to_json will need to include them as for third-party types.

  7. solvingj commented on Aug 11, 2017

    @solvingj
    Author

    Ok, finally got it working. It does seem it was a result of my lack of experience in C++.

    I did not realize I had to add the #include my json_adl_specialization.hpp file to any file that needs to serialize/deserialize any of the third-party types I've specialized. It makes sense now, but I thought that somehow the nlohmann library would automatically find and use my json_adl_specialization.hpp file because it had the nlohmann namespace. This is because I come from other languages, but also because much of what this library does seems like magic ;)

    So, maybe it's worth updating the README documentation to explain that part about including the json_adl_specialization.hpp along with json.hpp in the section about specializing for third-party types. Maybe that's obvious to most C++ developers though, not sure.

  8. theodelrieu commented on Aug 11, 2017

    @theodelrieu
    Contributor

    Yes I agree, the doc needs some clarifications for those use-cases!

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    solution: proposed fixa fix for the issue has been proposed and waits for confirmation

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions