@Override
 public void close() throws XMLStreamException {
   super.close();
   try {
     if (documentArray) {
       target.endArray();
     }
     target.close();
   } catch (IOException e) {
     throw new XMLStreamException("Close failed", e);
   }
 }
 @Override
 public void writeEndDocument() throws XMLStreamException {
   super.writeEndDocument();
   try {
     if (getScope().getInfo().isArray()) {
       target.endArray();
     }
     target.endObject();
   } catch (IOException e) {
     throw new XMLStreamException("Cannot end document", e);
   }
   getScope().getInfo().startObjectWritten = false;
 }
 @Override
 public void flush() throws XMLStreamException {
   try {
     target.flush();
   } catch (IOException e) {
     throw new XMLStreamException("Flush failed", e);
   }
 }
 public void writeEndArray() throws XMLStreamException {
   getScope().getInfo().endArray();
   try {
     target.endArray();
   } catch (IOException e) {
     throw new XMLStreamException("Cannot end array: " + getScope().getInfo().getArrayName(), e);
   }
 }
 public void writeStartArray(String fieldName) throws XMLStreamException {
   if (autoEndArray && getScope().getInfo().isArray()) {
     writeEndArray();
   }
   getScope().getInfo().startArray(fieldName);
   getScope().getInfo().pendingStartArray = false;
   try {
     if (!getScope().getInfo().startObjectWritten) {
       target.startObject();
       getScope().getInfo().startObjectWritten = true;
     }
     target.name(fieldName);
     target.startArray();
   } catch (IOException e) {
     throw new XMLStreamException("Cannot start array: " + fieldName, e);
   }
 }
 @Override
 protected void writeAttr(String prefix, String localName, String namespaceURI, String value)
     throws XMLStreamException {
   String name =
       XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)
           ? localName
           : prefix + namespaceSeparator + localName;
   try {
     if (!getScope().getInfo().startObjectWritten) {
       target.startObject();
       getScope().getInfo().startObjectWritten = true;
     }
     target.name('@' + name);
     target.value(value);
   } catch (IOException e) {
     throw new XMLStreamException("Cannot write attribute: " + name, e);
   }
 }
 @Override
 public void writeStartDocument(String encoding, String version) throws XMLStreamException {
   super.writeStartDocument(encoding, version);
   try {
     target.startObject();
   } catch (IOException e) {
     throw new XMLStreamException("Cannot start document", e);
   }
   getScope().getInfo().startObjectWritten = true;
 }
 @Override
 protected void writeNsDecl(String prefix, String namespaceURI) throws XMLStreamException {
   if (namespaceDeclarations) {
     try {
       if (!getScope().getInfo().startObjectWritten) {
         target.startObject();
         getScope().getInfo().startObjectWritten = true;
       }
       if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
         target.name('@' + XMLConstants.XMLNS_ATTRIBUTE);
       } else {
         target.name('@' + XMLConstants.XMLNS_ATTRIBUTE + namespaceSeparator + prefix);
       }
       target.value(namespaceURI);
     } catch (IOException e) {
       throw new XMLStreamException("Cannot write namespace declaration: " + namespaceURI, e);
     }
   }
 }
 @Override
 protected ScopeInfo writeStartElementTag(String prefix, String localName, String namespaceURI)
     throws XMLStreamException {
   ScopeInfo parentInfo = getScope().getInfo();
   if (parentInfo.hasData()) {
     if (!skipSpace || !isWhitespace(parentInfo.getData())) {
       throw new XMLStreamException(
           "Mixed content is not supported: '" + parentInfo.getData() + "'");
     }
     parentInfo.setData(null);
   }
   String fieldName = getFieldName(prefix, localName);
   if (getScope().isRoot() && getScope().getLastChild() != null && !documentArray) {
     if (!fieldName.equals(parentInfo.getArrayName())) {
       throw new XMLStreamException("Multiple roots within document");
     }
   }
   if (parentInfo.pendingStartArray) {
     writeStartArray(fieldName);
   }
   try {
     if (!parentInfo.isArray()) {
       if (!parentInfo.startObjectWritten) {
         target.startObject();
         parentInfo.startObjectWritten = true;
       }
     } else if (autoEndArray && !fieldName.equals(parentInfo.getArrayName())) {
       writeEndArray();
     }
     if (!parentInfo.isArray()) {
       target.name(fieldName);
     } else {
       parentInfo.incArraySize();
     }
   } catch (IOException e) {
     throw new XMLStreamException("Cannot write start element: " + fieldName, e);
   }
   return new ScopeInfo();
 }
 @Override
 protected void writeEndElementTag() throws XMLStreamException {
   try {
     if (getScope().getInfo().hasData()) {
       if (getScope().getInfo().startObjectWritten) {
         target.name("$");
       }
       target.value(getScope().getInfo().getData());
     }
     if (autoEndArray && getScope().getInfo().isArray()) {
       writeEndArray();
     }
     if (getScope().getInfo().startObjectWritten) {
       target.endObject();
     } else if (!getScope().getInfo().hasData()) {
       target.value(null);
     }
   } catch (IOException e) {
     throw new XMLStreamException(
         "Cannot write end element: "
             + getFieldName(getScope().getPrefix(), getScope().getLocalName()),
         e);
   }
 }
 @Override
 protected void writeData(Object data, int type) throws XMLStreamException {
   switch (type) {
     case XMLStreamConstants.CHARACTERS:
     case XMLStreamConstants.CDATA:
       if (getScope().isRoot() && !isStartDocumentWritten()) { // hack: allow to write simple value
         try {
           target.value(data);
         } catch (IOException e) {
           throw new XMLStreamException("Cannot write data", e);
         }
       } else {
         if (data == null) {
           throw new XMLStreamException("Cannot write null data");
         }
         if (getScope().getLastChild() != null) {
           if (!skipSpace || !isWhitespace(data)) {
             throw new XMLStreamException("Mixed content is not supported: '" + data + "'");
           }
         } else if (getScope().getInfo().hasData()) {
           if (data instanceof String) {
             getScope().getInfo().addText(data.toString());
           } else {
             throw new XMLStreamException("Cannot append primitive data: " + data);
           }
         } else {
           getScope().getInfo().setData(data);
         }
       }
       break;
     case XMLStreamConstants.COMMENT: // ignore comments
       break;
     default:
       throw new UnsupportedOperationException("Cannot write data of type " + type);
   }
 }