Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions release-notes/CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ Stefan Walter (@marvin9000)
* Requested #150: Allow specifying DOCTYPE declaration (`<!DOCTYPE root ...>`) to write
(3.2.0)

Joachim Lous (@jlous)
* Requested #207: Allow custom prefixes for Namespaces
(3.2.0)

Leonard Meyer (@LeonardMeyer)
* Reported #247: `@JacksonXmlRootElement` does not enforce the local name during
deserialization (add `XmlReadFeature.ENFORCE_ROOT_ELEMENT_NAME`)
Expand Down
5 changes: 5 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Version: 3.x (for earlier see VERSION-2.x)
#192: Two wrapped lists with items of same name conflict
(reported by @TeemuStenhammar)
(fix by @cowtowncoder, w/ Claude code)
#207: Allow custom prefixes for Namespaces
(requested by Joachim L)
(fix by @cowtowncoder, w/ Claude code)
#247: `@JacksonXmlRootElement` does not enforce the local name during
deserialization (add `XmlReadFeature.ENFORCE_ROOT_ELEMENT_NAME`)
(reported by Leonard M)
Expand Down Expand Up @@ -76,6 +79,8 @@ Version: 3.x (for earlier see VERSION-2.x)
polymorphic nested objects
(reported by Vitor P)
(fix by @cowtowncoder, w/ Claude code)
#541: Allow specifying URI of the default namespace for root element
(fix by @cowtowncoder, w/ Claude code)
#561: Deserializing empty timestamp fields to `null` value doesn't
work (instead becomes "empty", Epoch time)
(reported by @silvestr85)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package tools.jackson.dataformat.xml.ser;

import javax.xml.stream.XMLStreamException;

import org.codehaus.stax2.XMLStreamWriter2;

import tools.jackson.dataformat.xml.util.ArgUtil;

/**
* Entity representing binding from prefix to namespace URI, used
* to determine prefix to use for given namespace URI (and dynamically
* adding necessary declarations)
*
* @since 3.2
*/
public class NamespaceBinding
implements XmlGeneratorWritable
{
private final String _prefix;

private final String _namespaceURI;

public NamespaceBinding(String prefix, String namespaceURI) {
_prefix = ArgUtil.emptyToNull(prefix);
_namespaceURI = ArgUtil.nonEmptyNonNull("namespaceURI", namespaceURI);
}

@Override
public void write(ToXmlGenerator xmlGen, XMLStreamWriter2 sw) throws XMLStreamException {
if (_prefix == null) {
sw.setDefaultNamespace(_namespaceURI);
} else {
sw.setPrefix(_prefix, _namespaceURI);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,14 @@
package tools.jackson.dataformat.xml.ser;

import javax.xml.stream.XMLStreamException;

import org.codehaus.stax2.XMLStreamWriter2;

/**
* Common API for XML nodes -- Comments, DTDs, Processing Instructions -- to
* Base API for XML nodes -- Comments, DTDs, Processing Instructions -- to
* be written in Document Prolog (before actual XML Document (root node),
* after optional XML Declaration).
*
* @since 3.2
*/
public interface PrologDirective
extends XmlGeneratorWritable
{
/**
* Method to call to actually write out the directive using given
* {@link XMLStreamWriter2}. {@link ToXmlGenerator} is only passed
* in case access to configuration was needed.
*
* @param xmlGen Generator that called this method: MUST NOT call
* its output methods, only to be used for configuration access
* @param sw Writer to use for actual output of XML event
*/
public void write(ToXmlGenerator xmlGen, XMLStreamWriter2 sw)
throws XMLStreamException;
// All we need is so far in XmlGeneratorWritable
}
24 changes: 19 additions & 5 deletions src/main/java/tools/jackson/dataformat/xml/ser/ToXmlGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ public class ToXmlGenerator
/**********************************************************************
*/

/**
* Namespace bindings to add, if any.
*
* @since 3.2
*/
protected List<NamespaceBinding> _namespaceBindings;

/**
* XML directives (DTD, Comments, PIs) to write, if any.
*
Expand Down Expand Up @@ -228,16 +235,19 @@ public ToXmlGenerator(ObjectWriteContext writeCtxt, IOContext ioCtxt,

/**
* Method called by {@link XmlGeneratorInitializer} to inject
* necessary configuration.
* necessary configuration like Prolog Directives and namespace
* bindings.
*
* @since 3.2
*/
public void initProlog(boolean lfBetweenPrologDirectives,
List<PrologDirective> directives)
public void initDocument(boolean lfBetweenPrologDirectives,
List<PrologDirective> directives,
List<NamespaceBinding> nsBindings)
{
if (_initialized) { // sanity check
_reportError("Internal error: cannot call `initConfig()` after generator already initialized");
_reportError("Internal error: cannot call `initDocument()` after generator already initialized");
}
_namespaceBindings = nsBindings;
_lfBetweenPrologDirectives = lfBetweenPrologDirectives;
_prologDirectives = directives;
}
Expand Down Expand Up @@ -285,7 +295,11 @@ public void initGenerator() throws JacksonException
}
}
}

if (_namespaceBindings != null) {
for (NamespaceBinding ns : _namespaceBindings) {
ns.write(this, _xmlWriter);
}
}
} catch (XMLStreamException e) {
StaxUtil.throwAsWriteException(e, this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,25 @@
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;

protected boolean _addLfBetweenPrologDirectives = true;

protected boolean _hasDTD;

@Override
public void initialize(SerializationConfig config, JsonGenerator g) throws JacksonException {
if (g instanceof ToXmlGenerator xg) {
xg.initProlog(_addLfBetweenPrologDirectives, _directives);
xg.initDocument(_addLfBetweenPrologDirectives, _directives,
_namespaceBindings);
}
}

Expand Down Expand Up @@ -124,6 +133,39 @@ 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;
}

protected XmlGeneratorInitializer _add(PrologDirective d) {
if (_directives == null) {
_directives = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package tools.jackson.dataformat.xml.ser;

import javax.xml.stream.XMLStreamException;

import org.codehaus.stax2.XMLStreamWriter2;

/**
* Common API for XML entities to write via {@link ToXmlGenerator}.
*
* @since 3.2
*/
public interface XmlGeneratorWritable
{
/**
* Method to call to actually write out the entity using given
* {@link XMLStreamWriter2}. {@link ToXmlGenerator} is only passed
* in case access to configuration was needed.
*
* @param xmlGen Generator that called this method: MUST NOT call
* its output methods, only to be used for configuration access
* @param sw Writer to use for actual output of XML event
*/
public void write(ToXmlGenerator xmlGen, XMLStreamWriter2 sw)
throws XMLStreamException;
}
Loading