@Override
    public String serializeToString(Element root) {
        if (root == null) {
            return null;
        }
        
        if (root.getGUID() == null) {
            root.setGUID(guidCreator.getNewGUID());
        }
        
        StringBuilder sb = new StringBuilder("{");
        
        if (!root.isMirroring()) {
            sb.append(String.format(TAG_FMT, root.getTag()));
            
            if (root.getNumberOfAttributes() > 0) {
                sb.append(", \"attributes\": [");
                String attName, attVal;
                for (int i = 0; i < root.getNumberOfAttributes(); i++) {
                    attName = root.getAttributeName(i);
                    attVal = root.getAttribute(attName);
                    sb.append(String.format(ATTRIBUTE_FMT, attName, attVal));
                    if (i + 1 < root.getNumberOfAttributes()) {
                        sb.append(", ");
                    }
                }
                sb.append("]");
            }
            
            if (root.hasChildren()) {
                sb.append(", \"children\": [");
                for (Element child : root.children()) {
                    sb.append(serializeToString(child));
                }
                sb.append("]");
            }
        } else {
            Element mirroredElem = root.getMirroredElement();
            String guid = mirroredElem.getGUID();
            sb.append(String.format("\"mirroring\": \"%s\"", guid));
        }
        
        sb.append("}");
        return sb.toString();
    }