ValueTree array to xml

I use code like this to put arbitrary objects in arrays into a ValueTree.

template <typename T>
void serializeArray(ValueTree & target, const T & source)
{
	for (auto & l : source)
	{
		auto t = l->getSerialized();
		target.addChild(t, -1, nullptr);
	}
}

Maybe something like this adaption works for plain data types if you just want to copy data into a ValueTree in a useful way:

template <typename T>
void serializeArray(ValueTree & target, const T & source)
{
	for (auto & l : source)
	{
		ValueTree e{"element"};
		e.setProperty("value", l, nullptr);
		target.addChild(e, -1, nullptr);
	}
}

Or sometimes I use this, although I’m not sure there wasn’t a bug in it ;_) But conceptually this is an array of ItemType stored in an array where the ItemType has a constructor that takes a ValueTree into which it stores all it’s data. This has the advantage that the data is backended onto the ValueTree properly so the ValueTree is always up to date and all the listener stuff works, and things can share the same data.

It probably needs an example…

template <class ItemType>
class ValueTreeArray
{
public:

	class Iterator
	{
	public:
		Iterator(const ValueTreeArray* ptr, int pos)
			: pos(pos), ptr(ptr)
		{ }
		bool operator!= (const Iterator& other) const { return pos != other.pos; }
		ItemType operator* () const { return ptr->get(pos); };
		const Iterator& operator++ () { ++pos; return *this; }
	private:
		int pos;
		const ValueTreeArray* ptr;
	};


	ValueTreeArray(const String & nodeTypeName, const ValueTree & treeForData)
		:
		nodeTypeName(nodeTypeName),
		tree(treeForData)
	{
		valueTreeAction.setValueTree(tree); 
		valueTreeAction.setAction(
			[this]()
		{
			listeners.call(&Listener::arrayDataUpdated); 
		});

		jassert(tree != ValueTree::invalid);
	}

	~ValueTreeArray() {}

	ItemType operator[] (int index)
	{
		return ItemType(tree.getChild(index));
	}
	ItemType get(int index) const
	{
		return ItemType(tree.getChild(index));
	}

	Iterator begin() const { return Iterator(this, 0); }
	Iterator end() const { return Iterator(this, size()); }
	int size() const { return tree.getNumChildren(); }

	ItemType getNewNode()
	{
		ValueTree itemTree(nodeTypeName);
		tree.addChild(itemTree, -1, nullptr);
		ItemType newItem(itemTree);
		return newItem;
	}

	/** Inserts something after the given location. */
	ItemType insertAfter(int location)
	{
		ValueTree itemTree(nodeTypeName); 
		tree.addChild(itemTree, location + 1, nullptr);
		ItemType newItem(itemTree);
		return newItem;
	}


	/** Calls getTree() on the ItemType and adds it to the
	array. */
	void add(ItemType item)
	{
		tree.addChild(item.getTree(), -1, nullptr);
	}

	void remove(int index) { tree.removeChild(index, nullptr); }
	void clear() { tree.removeAllChildren(nullptr); } 

	bool operator== (const ValueTreeArray& rhs) const
	{
		return tree.isEquivalentTo(rhs.tree);
	}

	class Listener
	{
	public:
		virtual ~Listener() {}
		virtual void arrayDataUpdated() = 0; 
	};

	void addListener(Listener * listener)
	{
		listeners.add(listener);
	}

private:
	ListenerList<Listener> listeners;
	ValueTreeActionOnAnyChange valueTreeAction;
	String nodeTypeName;
	ValueTree tree;
	JUCE_LEAK_DETECTOR(ValueTreeArray)
};