ValueTree, XML for plugin state, for use with complex data?


It seems that going to/from XML in the setting and getting of plugin state info has some good benefits. Mainly that a human readable representation of the ValueTree is readily at hand.
I’m at the point where I’d like to store more complex data structures (beyond string/value pairs)

I hit this assertion after adding a value that was a var() referenceCountedObject:

void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const
    for (auto& i : values)
            // These types can't be stored as XML!
            jassert (! i.value.isObject());

From what I can see ahead, I will need to write and maintain a fair amountof code for saving and restoring of data structures.
Is there a smart way of doing this that I would want to learn about? (I’m all for learning!)



Yes, you will need to write the serialisation and deserialisation of your object yourself.
The easiest way is, since you know which objects are to be expected, to add the reading manually in getStateInformation() to the tree, like

state.setProperty ("object", object.toValueTree(), nullptr);


In the processor constructor:

    parameters.state.setProperty(myCPPropID, var(), nullptr);
	myCPProp.referTo(parameters.state.getPropertyAsValue(myCPPropID, nullptr, false));
	cp = new CPProperties();
	cp->name = "buddy";
	cp->colour = Colours::red;

(btw; does this look like it would actually work?)
The assertion I posted before happens in

void myAudioProcessor::getStateInformation(MemoryBlock& destData)
{                                     //parameters->data block (i.e save to project)

	auto state = parameters.copyState();
	std::unique_ptr<XmlElement> xml(state.createXml());  //assertion happens here

So the object I added couldn’t be turned into XML, I assume.
You indicate the custom serialise method is called in vt.setProperty().
Roughly speaking what would this method look like for a simple struct like I show above.
I’m assuming it would render the data as values that are compatible with the createXml() (and fromXml() to deserialise) ?



Yes, I would not put the object into the tree, but rather it’s state.
I was thinking, if there is something it tries to call when serialising the ValueTree, but I don’t think so. You cannot avoid this assert with an object in the state.



So I add a node to the valuetree that represents an object with maybe the nodetype to indicate the object type, and a list of property/value pairs to represent the state of items within the object?

I suppose this implies that var(&referenceCountedObject) is not useful in the case of APVTS though it may be in the general ValueTree case?



It is not the APVTS, that gives the problems, it’s serialisation, that is just not available for objects.

A polymorph serialisation method in ReferenceCountedObject would be needed, but since that is used in so many places, I doubt it will be added.



Thanks for your input.
Just to confirm: a serialise and deserialise method is written for each object type as required, and when it’s done this way the XML translation will yield a human readable version of the plugin tree state. The “readability” of the text values in the XML will of course be dependent on the serialisation technique in my code.



exactly. My objects usually have two methods and a constructor:

MyObject (const ValueTree& state) { setState (state); }

void setState (const ValueTree& state);

ValueTree getState() const;

and a ValueTree::Listener reacts to propertyChanged applying it to
setState (treeWhosePropertyHasChanged);
And a parent could create a new object, when a valueTreeAdded() is called etc. That way you can easily react to an undo manager.



To follow your pattern:

  • Constructor creates a node that represents the object’s state. Adds the node to the VT state. This is done in the processor constructor after the plugin parameters have been made.

  • setState() updates/loads the object’s state from the VT

  • getState() transfers the object’s state into property/value pairs in a node then returns the node.

It follows that setState() would be called from setStateInformation() (load from host) and getState() would be called from getStateInformation()(save to host)?