Skip to content

Latest commit

 

History

History
185 lines (145 loc) · 5.14 KB

class.md

File metadata and controls

185 lines (145 loc) · 5.14 KB

Classes

Arbitrary C++ data structures are supported and can be described using the trait json_data_contract.

Basic class mapping

Below is an ordinary JSON object

{
  "member0": "this is a test",
  "member1": 314159,
  "member2": true
}

The above JSON describes a class with three members, a string named member0, an integer named member1, and a boolean named member2

Below is the C++ data structure and the trait to map the members to that of the JSON object. Note that the names of the C++ data members do not have to be the same as the JSON object's. To see a working example using this code, refer to cookbook_class1_test.cpp

struct MyClass1 {
  std::string member_0;
  int member_1;
  bool member_2;
};

namespace daw::json {
  template<>
  struct json_data_contract<MyClass1> {
    using type = json_member_list<
      json_string<"member0">, 
      json_number<"member1", int>,
      json_bool<"member2">
    >;

    static inline auto to_json_data( MyClass1 const &value ) {
      return std::forward_as_tuple( 
        value.member_0, 
        value.member_1,
        value.member_2 );
    }
  };
}

The above json_data_contract trait maps the JSON members to the constructor of MyClass1 in the order specified. The arguments of type std::string, int, bool will be passed.

Class as a member

The serializing and deserializing is recursive. So if a class contains another class, the requirement is that that class has been mapped already. Assuming we already have the mapping above, lets embed that into another class

{
  "a": {
    "member0": "this is a test",
    "member1": 314159,
    "member2": true
  },
  "b": 1234
}

The JSON object that has a member "a" that matches MyClass1, and a second member is an unsigned To see a working example using this code, refer to cookbook_class2_test.cpp

// Code from previous MyClass1 example

struct MyClass2 {
  MyClass1 a;
  unsigned b;
};

namespace daw::json {
  template<>
  struct json_data_contract<MyClass2> {
    using type = json_member_list<
      json_class<"a", MyClass1>,
      json_number<"b", unsigned>
    >;

    static inline auto
    to_json_data( MyClass2 const &value ) {
      return std::forward_as_tuple( value.a, value.b );
    }
  };
} // namespace daw::json

Selective mapping

Not all the JSON objects members need to be mapped. Below is the same JSON object as in the MyClass2 example above. To see a working example using this code, refer to cookbook_class3_test.cpp

{
  "a": {
    "member0": "this is a test",
    "member1": 314159,
    "member2": true
  },
  "b": 1234
}
// Code from previous MyClass1 example

struct MyClass3 {
  MyClass1 a;
};

namespace daw::json {
  template<>
  struct json_data_contract<MyClass3> {
    using type = json_member_list<json_class<"a", MyClass1>>;

    static inline auto
    to_json_data( MyClass3 const &value ) {
      return std::forward_as_tuple( value.a );
    }
  };
}

Only the "a" member is mapped, the "b" member of the JSON object is ignored.

Alternate Class Mappings

If a type is already mapped or has many possible JSON mappings you can provide an alternate mapping. This is accomplished by mapping to the daw::json::json_alt<Type, Index> type. The optional index allows for any number of mappings that fit into a std::size_t.

To see a working example using this code, refer to cookbook_class_alternate1_test.cpp

struct Thing {
	int a;
};
namespace daw::json {
	template<>
	struct json_data_contract<Thing> {
		using type = json_member_list<json_number<"a", int>>;
	};
	
	struct json_data_contract<json_alt<Thing>> {
		using type = json_ordered_member_list<int>; 
	};
}
//...
Thing a = daw::json::from_json<Thing>( json_string_a );
Thing b = daw::json::from_json<daw::json::json_alt<Thing>>( json_string_b );

Class construction

If constructing the class requires extra steps than passing the parsed members to it's constructor one can provide a custom Constructor callable as the default for that type or for a specific json_class mapping.

Specifying the classes default

If one wants to change the default Constructor type of a specific type they can add a type alias in that classes json_data_contract. For instance, if a member needs extra checking that the types constructor does not provide:

struct FooConstructor {
  constexpr Foo operator( )( std::string name, .... ) const {
    if( name != "bobfred" ) { throw badname{}; }
    return Foo{ name, .... };
  }
};

template<>
struct json_data_constract<Foo> {
  using constructor_t = FooConstructor;
  using type = json_member_list<json_string<"name">,...>;
};

Here the type Foo will use a callable of type FooConstructor to create all Foo's in the library unless overridden by a mapping.

Per mapping

The json_class mapping's second parameter is that of a Constructor type to call to construct that class and override the default. It can be specified like

  json_class<"name", FooConstructor>