XmlGeneratorInitializer.java
package tools.jackson.dataformat.xml.ser;
import java.util.ArrayList;
import java.util.List;
import javax.xml.namespace.QName;
import tools.jackson.core.JacksonException;
import tools.jackson.core.JsonGenerator;
import tools.jackson.core.exc.StreamWriteException;
import tools.jackson.databind.*;
import tools.jackson.databind.cfg.GeneratorInitializer;
import tools.jackson.dataformat.xml.XmlWriteFeature;
/**
* Default {@link GeneratorInitializer} implementation to use with
* {@link ToXmlGenerator}, registered via
* {@link ObjectWriter#with(GeneratorInitializer)}.
* It allows output of various document-level things such as
*<ul>
* <li>Document Type Declarations (DTD); that is "<!DOCTYPE>" directive
* </li>
* <li>Comments (in Document prolog, before the root element)
* </li>
* <li>Namespace bindings (prefix to URI mappings)
* </li>
* <li>Processing Instructions (PIs; in Document prolog, before the root element)
* </li>
* <li>XML Declaration (with custom version, encoding and/or standalone value)
* </li>
* </ul>
*<p>
* NOTE: instances are mutable, not thread-safe.
*
* @since 3.2
*/
public class XmlGeneratorInitializer
implements GeneratorInitializer
{
/**
* Prolog Directives to pass for generator to write.
*/
protected List<PrologDirective> _directives;
/**
* Namespace bindings (prefix to URI) to register with generator.
*/
protected List<NamespaceBinding> _namespaceBindings;
/**
* Attributes to add to root element (if any).
*
* @since 3.2
*/
protected List<RootAttribute> _rootAttributes;
/**
* Custom XML declaration to write.
*/
protected XmlDeclaration _xmlDeclaration;
protected boolean _addLfBetweenPrologDirectives = true;
protected boolean _hasDTD;
@Override
public void initialize(SerializationConfig config, JsonGenerator g) throws JacksonException {
if (g instanceof ToXmlGenerator xg) {
xg.initDocument(_xmlDeclaration,
_addLfBetweenPrologDirectives, _directives,
_namespaceBindings, _rootAttributes);
}
}
/**
* Method to change whether line-feeds are to be added between Prolog directives
* or not: default being they are (enabled).
*
* @param addLFs Whether line-feeds are to be added or not (default: {@code true})
*
* @return This initializer for call chaining
*/
public XmlGeneratorInitializer linefeedsBetweenPrologDirectives(boolean addLFs) {
_addLfBetweenPrologDirectives = addLFs;
return this;
}
/**
* Method for adding XML comment; to be written at position added
* relative to other directives
* (but always after XML Declaration which must come before any other output;
* and before Document Root element)
*
* @param commentContent (optional) Comment content to include
*
* @return This initializer for call chaining
*/
public XmlGeneratorInitializer addComment(String commentContent) {
return _add(new PrologComment(commentContent));
}
/**
* Convenience method that constructs {@link DTD} out of arguments
* and calls {@link #addDTD(DTD)}.
*
* @param rootName (required) Root name for DTD
* @param systemId (optional) System Id for DTD
* @param publicId (optional) Public Id for DTD
* @param internalSubset (optional) Internal subset for DTD (not including
* surrounding brackets
*
* @return This initializer for call chaining
*/
public XmlGeneratorInitializer addDTD(String rootName,
String systemId, String publicId,
String internalSubset) {
return addDTD(new DTD(rootName, systemId, publicId, internalSubset));
}
/**
* Method for adding Document Type Declaration (DTD) directive; to
* be written at position added relative to other directives
* (but always after XML Declaration which must come before any other output;
* and before Document Root element)
*
* @param dtd DTD to write
*
* @return This initializer for call chaining
*/
public XmlGeneratorInitializer addDTD(DTD dtd) {
if (_hasDTD) {
throw new StreamWriteException(null, "Cannot add another `DTD`, initializer already has one");
}
_hasDTD = true;
return _add(dtd);
}
/**
* Method for adding XML Processing Instruction (PI); to be written at
* position added relative to other directives
* (but always after XML Declaration which must come before any other output;
* and before Document Root element)
*
* @param target Processing Instruction target: must not be {@code null} or
* empty String
* @param data (optional) Processing Instruction data part, if any,
* separated by a space from target (if not null)
*
* @return This initializer for call chaining
*/
public XmlGeneratorInitializer addPI(String target, String data) {
return _add(new PrologPI(target, data));
}
/**
* Method for specifying namespace URI to preferentially bind to the
* "default namespace" (one used when element has no prefix).
* This will guide underlying generator to add necessary
* declarations when actually writing elements with matching
* namespace URI.
*
* @param namespaceURI URI of the default namespace
*
* @return This initializer for call chaining
*/
public XmlGeneratorInitializer addDefaultNamespace(String namespaceURI) {
return addNamespace(null, namespaceURI);
}
/**
* Method for adding a mapping (binding) between given prefix and matching
* namespace URI. This will guide underlying generator to add necessary
* declarations when actually writing namespaced attributes and elements.
*
* @param prefix Prefix to use for namespace
* @param namespaceURI URI of the namespace
*
* @return This initializer for call chaining
*/
public XmlGeneratorInitializer addNamespace(String prefix, String namespaceURI) {
if (_namespaceBindings == null) {
_namespaceBindings = new ArrayList<>();
}
_namespaceBindings.add(new NamespaceBinding(prefix, namespaceURI));
return this;
}
/**
* Method for adding an attribute to be written on the root element of
* the output document. Attributes are emitted in the order added,
* after the root element's start tag is written and before any
* content from the value being serialized.
*<p>
* Typical use case is adding XML Schema instance attributes such as
* {@code xsi:schemaLocation} or {@code xsi:noNamespaceSchemaLocation}.
*<p>
* NOTE: root attributes are only emitted when the root value being
* serialized produces a structured (object) start element; scalar
* root values do not currently get root attributes attached.
*
* @param name Attribute name (with optional namespace and prefix)
* @param value Attribute value (null is coerced to empty String)
*
* @return This initializer for call chaining
*
* @since 3.2
*/
public XmlGeneratorInitializer addRootAttribute(QName name, String value) {
if (_rootAttributes == null) {
_rootAttributes = new ArrayList<>();
}
_rootAttributes.add(new RootAttribute(name, value));
return this;
}
/**
* Convenience overload of {@link #addRootAttribute(QName, String)} for
* adding non-namespaced attribute by local name.
*
* @param localName Attribute local name (must be non-empty) in default
* namespace (one with URI of "")
* @param value Attribute value (if {@code null}, coerced to empty String)
*
* @return This initializer for call chaining
*
* @since 3.2
*/
public XmlGeneratorInitializer addRootAttribute(String localName, String value) {
return addRootAttribute(new QName(localName), value);
}
/**
* Method for specifying custom XML declaration to write.
*<p>
* When a custom XML declaration is registered it fully replaces output
* that would otherwise be produced by
* {@link XmlWriteFeature#WRITE_XML_DECLARATION},
* {@link XmlWriteFeature#WRITE_XML_1_1} and
* {@link XmlWriteFeature#WRITE_STANDALONE_YES_TO_XML_DECLARATION}:
* those format features are ignored. Note that caller is responsible
* for ensuring the declared encoding matches the encoding the
* underlying {@code Writer} or {@code OutputStream} actually uses.
*
* @param version XML version: either "1.0" or "1.1"
* @param encoding {@code encoding} content will be encoded in: usually "UTF-8"
*
* @return This initializer for call chaining
*/
public XmlGeneratorInitializer addXmlDeclaration(String version, String encoding) {
return addXmlDeclaration(new XmlDeclaration(version, encoding, null));
}
/**
* Method for specifying custom XML declaration to write.
*<p>
* See {@link #addXmlDeclaration(String, String)} for details on how
* this interacts with {@link XmlWriteFeature} flags.
*
* @param version XML version: either "1.0" or "1.1"
* @param encoding {@code encoding} content will be encoded in: usually "UTF-8"
* @param standalone {@code standalone} pseudo-attribute value to write
*
* @return This initializer for call chaining
*/
public XmlGeneratorInitializer addXmlDeclaration(String version, String encoding,
boolean standalone) {
return addXmlDeclaration(new XmlDeclaration(version, encoding, standalone));
}
/**
* Method for specifying custom XML declaration to write.
*<p>
* See {@link #addXmlDeclaration(String, String)} for details on how
* this interacts with {@link XmlWriteFeature} flags.
*
* @param xmlDeclaration declaration to write
*
* @return This initializer for call chaining
*/
public XmlGeneratorInitializer addXmlDeclaration(XmlDeclaration xmlDeclaration) {
if (_xmlDeclaration != null) {
throw new StreamWriteException(null,
"Cannot add another XML Declaration, initializer already has one");
}
_xmlDeclaration = xmlDeclaration;
return this;
}
protected XmlGeneratorInitializer _add(PrologDirective d) {
if (_directives == null) {
_directives = new ArrayList<>();
}
_directives.add(d);
return this;
}
}