Open rcdailey opened 8 years ago
Also this solution does not work if myclass
is const (in conjunction with output archives, which is normally valid).
There's no built in mechanism for this but you could modify xml.hpp or json.hpp to achieve what you want. In the constructors for the output archives we start the root node, which you could selectively disable by adding some parameters to the options struct.
You may have problems loading because you won't be loading valid XML/JSON documents. You could likely modify the input archives to handle this as well.
I think you are missing the idea here. I'm not suggesting we remove the root XML node. But rather, the 2nd child that is created. Example:
<value0>
<value0>
<value0>foo</value0>
<value1>bar</value1>
</value0>
</value0>
The 2nd child is value0
and unnecessary. It is only added because I do archive(myclass)
. Instead, I want the XML output like:
<value0>
<value0>foo</value0>
<value1>bar</value1>
</value0>
This isn't invalid XML.
Any thoughts @AzothAmmo ?
I haven't had time to look into this much yet, but I do understand what you want to do. Without thinking through all of the implications, I think this could potentially be implemented via a wrapper class that you would use something like:
ar( cereal::make_minimal( myData ) );
Which would cause myData
to be serialized without an explicit node for itself as in your example.
I think this is a lot cleaner (implementation-wise) than adding new serialization function variants.
I guess my point is, the current behavior seems undesirable. Is there a reason to have another root under the root you already add at the archive level? Isn't it redundant? I can't think of a single use case for it.
It seems like the latter example output I provided in an earlier reply should be the default behavior.
Unless the use case is to support minimal types passed directly to the archive? I can understand then, but doesn't seem like a practical scenario.
I like your idea of flexibility I am just questioning if the current behavior has valid use cases.
It is the way it is because we need to support serializing multiple objects to the same archive.
That's true! Good point. I'll look forward to your suggested solution.
On Mon, Jul 11, 2016, 6:45 PM Shane Grant notifications@github.com wrote:
It is the way it is because we need to support serializing multiple objects to the same archive.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/USCiLab/cereal/issues/303#issuecomment-231898563, or mute the thread https://github.com/notifications/unsubscribe/ABr6doe0DgCStNCOa4DTAUfcdT1H6WxMks5qUtWtgaJpZM4I7E-l .
So I am working on a make_minimal
implementation, however I'm not sure how I should do it. I have a rough idea I'm trying to make work:
namespace cereal
{
namespace detail
{
template<typename T>
class minimal_wrapper
{
public:
minimal_wrapper(T& obj)
: m_obj(obj)
{}
template<typename Archive>
std::string save_minimal(Archive const& ar)
{
// save here somehow
}
template<typename Archive>
void load_minimal(Archive const& ar, std::string const& value)
{
// load here somehow
}
private:
T& m_obj;
};
}
template<typename T>
detail::minimal_wrapper<T> make_minimal(T& obj)
{
return detail::minimal_wrapper<T>{obj};
}
}
However, I'm not sure how I should implement save_minimal
and load_minimal
in the wrapper. Because the archive is const
, I guess that means I can't use it (not even sure why it's passed in to begin with).
Do you have any ideas? I want to implement this externally first and see how it works, then I'm happy to do a PR.
My idea was more along the lines of how NameValuePair
and the traits for xxx_minimal
work. Essentially the class would be a very thin wrapper that is detected by prologue/epilogue functions and sets up state appropriately.
Naming it make_minimal
was not a good choice on my part. You can't directly use save or load minimal because these expect a single value to be serialized.
I'm a bit worried that there may be ways to break this mechanism through some combination of nesting it and using out of order loading.
What this is effectively enabling is a "passthrough" to children, or a "make children into siblings" functionality.
In that case, wouldn't it be reasonable to make it function as if the intermediate class doesn't exist at all? The children will be treated as siblings to the parent that initiated its serialize function.
But honestly this isn't something we need to overgeneralize. I can't even make a good argument for using this functionality outside of the root level invocation to the archive.
Can't we just make it a simple on/off flag at the archive level?
MyObj myobj;
std::istringstream ss{"some json data"};
cereal::JSONInputArchive archive(ss, no_siblings); // 'no_siblings' could be an enumerator provided by cereal; one of many to add constraints to or change behavior of archives
archive(myobj);
Effectively this forces the serializable class (MyObj
in this case) to write the root-level node. It is also an error to use an archive with this option set for more than 1 sibling object (you can detect this and throw for num args > 1.
Much simpler, less scenarios to consider. Similar to this, maybe a CRTP derived class that can be used to change this behavior:
cereal::SingleUseArchive<cereal::JSONInputArchive> archive(ss);
archive(myobj);
archive(mysecondobj); // throws
Food for thought...
@AzothAmmo Would it be possible for you to help me come up with a solution to your make_minimal
idea? I'm happy to help contribute a change, I just need some help getting started.
This is a huge problem right now for me so I need a solution.
If you are using external serialize function, u also can fix that problem calling this function directly:
template <class Archive>
void serialize(Archive & ar, ReporRegistrationData& d)
{
ar(cereal::make_nvp("app_id", d.app_id),
cereal::make_nvp("app_version", d.app_version),
cereal::make_nvp("hw_id", d.hw_id),
cereal::make_nvp("event_type", d.event_type),
);
}
And then for serialization:
std::stringstream stream(std::ios_base::out | std::ios_base::binary);
{
cereal::JSONOutputArchive serializer(stream);
cereal::serialize<cereal::JSONOutputArchive>(serializer, data);
}
const auto res = stream.str();
for deserialization (because cereal also doesn't wanna deserialize without enclosing parent node ):
std::stringstream stream(std::ios_base::in | std::ios_base::out);
stream.write(json.data(), json.size());
cereal::JSONInputArchive deserializer(stream);
cereal::serialize<cereal::JSONInputArchive>(deserializer, data);
Hope it will help somebody.
Hi there. I wrote simple header that help to perform inline serialization (without versioning): https://github.com/LowCostCustoms/cereal-inline/blob/master/cereal_inline.hpp
@LowCostCustoms is a versioning supported version hard to make? I took a look but immediately ran into a bit of trouble so thought I'd ask if anyone else has tried?
I'm running into this with serializing a std::vector
. I get:
{
"value0": [
1,
2,
3
]
}
But I want:
[
1,
2,
3
]
This was my first test with cereal, and I can't figure out how to get what I want. It might be back to rapidjson for me. :(
@wrightleft
Give you vector a meaningful name via make_nvp
I want to write my object to json or xml archive without the preliminary wrapper node around the root-level data. This situation is covered perfectly in this stack overflow question:
http://stackoverflow.com/questions/33726072/how-to-serialize-a-json-object-without-enclosing-it-in-a-sub-object-using-cereal
My sample code:
The above yields the wrapping object which I do not want (named
"value0"
). Per the SO answer linked previously, the solution is:However, this will not work if there are separate save/load functions or other types of callbacks.
Is there a built-in mechanism to disable the root node on
develop
?