From c11e8bcdca38ad9e1ad38957d65e4e40fa5c58e2 Mon Sep 17 00:00:00 2001 From: Croway Date: Mon, 19 Feb 2024 14:52:46 +0100 Subject: [PATCH 1/4] Add EMPTY_ELEMENT_AS_EMPTY_ARRAY Feature --- .../dataformat/xml/deser/FromXmlParser.java | 13 ++++++++++++ .../dataformat/xml/deser/XmlTokenStream.java | 5 +++++ .../xml/deser/EmptyStringValueTest.java | 20 +++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java index f819caa1d..faabc22ce 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java @@ -86,6 +86,19 @@ public enum Feature implements FormatFeature */ EMPTY_ELEMENT_AS_NULL(false), + /** + * Feature that indicates whether XML Empty elements (ones where there are + * no separate start and end tags, but just one tag that ends with "/>") + * are exposed as {@link JsonToken#START_ARRAY} {@link JsonToken#END_ARRAY}) or not. If they are not + * returned as `[]` tokens, they will be returned as {@link JsonToken#VALUE_STRING} + * tokens with textual value of "" (empty String). + *

+ * Default setting is {@code false} + * + * @since 2.9 + */ + EMPTY_ELEMENT_AS_EMPTY_ARRAY(false), + /** * Feature that indicates whether XML Schema Instance attribute * {@code xsi:nil} will be processed automatically -- to indicate {@code null} diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java index 8e1320165..56e5fd296 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java @@ -5,6 +5,7 @@ import javax.xml.XMLConstants; import javax.xml.stream.*; +import com.fasterxml.jackson.core.JsonToken; import org.codehaus.stax2.XMLStreamLocation2; import org.codehaus.stax2.XMLStreamReader2; @@ -549,6 +550,7 @@ private final int _next() throws XMLStreamException /** * @return Collected text, if any, EXCEPT that if {@code FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL} + * OR {@code FromXmlParser.Feature.EMPTY_ELEMENT_AS_EMPTY_ARRAY} * AND empty element, returns {@code null} */ private final String _collectUntilTag() throws XMLStreamException @@ -559,6 +561,9 @@ private final String _collectUntilTag() throws XMLStreamException if (FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL.enabledIn(_formatFeatures)) { return null; } + if (FromXmlParser.Feature.EMPTY_ELEMENT_AS_EMPTY_ARRAY.enabledIn(_formatFeatures)) { + return JsonToken.START_ARRAY.asString() + JsonToken.END_ARRAY.asString(); + } return ""; } diff --git a/src/test/java/com/fasterxml/jackson/dataformat/xml/deser/EmptyStringValueTest.java b/src/test/java/com/fasterxml/jackson/dataformat/xml/deser/EmptyStringValueTest.java index 79f729719..509ac26e7 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/xml/deser/EmptyStringValueTest.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/xml/deser/EmptyStringValueTest.java @@ -85,6 +85,26 @@ public void testEmptyElement() throws Exception assertEquals("", name.last); } + public void testEmptyElementEmptyArray() throws Exception + { + final String XML = ""; + + // Default settings (since 2.12): empty element does NOT become `null`: + Name name = MAPPER.readValue(XML, Name.class); + assertNotNull(name); + assertEquals("", name.first); + assertEquals("", name.last); + + // but can be changed + XmlMapper mapper2 = XmlMapper.builder() + .enable(FromXmlParser.Feature.EMPTY_ELEMENT_AS_EMPTY_ARRAY) + .build(); + name = mapper2.readValue(XML, Name.class); + assertNotNull(name); + assertEquals("[]", name.first); + assertEquals("", name.last); + } + public void testEmptyStringElement() throws Exception { // then with empty element From 371fda9ce6a3b5fc0b7b7a962c9b3fb55f0a6456 Mon Sep 17 00:00:00 2001 From: Croway Date: Tue, 20 Feb 2024 11:32:36 +0100 Subject: [PATCH 2/4] Make empty values configurable --- .../jackson/dataformat/xml/XmlFactory.java | 42 +++++++++++++++---- .../dataformat/xml/XmlFactoryBuilder.java | 16 +++++++ .../jackson/dataformat/xml/XmlMapper.java | 9 ++++ .../dataformat/xml/deser/FromXmlParser.java | 36 +++++++++------- .../dataformat/xml/deser/XmlTokenStream.java | 15 ++++--- .../xml/deser/EmptyStringValueTest.java | 2 +- 6 files changed, 91 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java index 521c9bded..22b93af27 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java @@ -66,6 +66,8 @@ public class XmlFactory extends JsonFactory protected String _cfgNameForTextElement; + protected String _cfgValueForEmptyElement; + protected XmlNameProcessor _nameProcessor; /* @@ -107,18 +109,26 @@ public XmlFactory(ObjectCodec oc, XMLInputFactory xmlIn, XMLOutputFactory xmlOut public XmlFactory(ObjectCodec oc, int xpFeatures, int xgFeatures, XMLInputFactory xmlIn, XMLOutputFactory xmlOut, String nameForTextElem) { - this(oc, xpFeatures, xgFeatures, xmlIn, xmlOut, nameForTextElem, XmlNameProcessors.newPassthroughProcessor()); + this(oc, xpFeatures, xgFeatures, xmlIn, xmlOut, nameForTextElem, FromXmlParser.DEFAULT_EMPTY_ELEMENT_VALUE, XmlNameProcessors.newPassthroughProcessor()); + } + + public XmlFactory(ObjectCodec oc, int xpFeatures, int xgFeatures, + XMLInputFactory xmlIn, XMLOutputFactory xmlOut, + String nameForTextElem, String valueForEmptyElement) { + this(oc, xpFeatures, xgFeatures, xmlIn, xmlOut, nameForTextElem, valueForEmptyElement, + XmlNameProcessors.newPassthroughProcessor()); } protected XmlFactory(ObjectCodec oc, int xpFeatures, int xgFeatures, XMLInputFactory xmlIn, XMLOutputFactory xmlOut, - String nameForTextElem, XmlNameProcessor nameProcessor) + String nameForTextElem, String valueForEmptyElement, XmlNameProcessor nameProcessor) { super(oc); _nameProcessor = nameProcessor; _xmlParserFeatures = xpFeatures; _xmlGeneratorFeatures = xgFeatures; _cfgNameForTextElement = nameForTextElem; + _cfgValueForEmptyElement = valueForEmptyElement; if (xmlIn == null) { xmlIn = StaxUtil.defaultInputFactory(getClass().getClassLoader()); // as per [dataformat-xml#190], disable external entity expansion by default @@ -145,6 +155,7 @@ protected XmlFactory(XmlFactory src, ObjectCodec oc) _xmlParserFeatures = src._xmlParserFeatures; _xmlGeneratorFeatures = src._xmlGeneratorFeatures; _cfgNameForTextElement = src._cfgNameForTextElement; + _cfgValueForEmptyElement = src._cfgValueForEmptyElement; _xmlInputFactory = src._xmlInputFactory; _xmlOutputFactory = src._xmlOutputFactory; _nameProcessor = src._nameProcessor; @@ -161,6 +172,7 @@ protected XmlFactory(XmlFactoryBuilder b) _xmlParserFeatures = b.formatParserFeaturesMask(); _xmlGeneratorFeatures = b.formatGeneratorFeaturesMask(); _cfgNameForTextElement = b.nameForTextElement(); + _cfgValueForEmptyElement = b.valueForEmptyElement(); _xmlInputFactory = b.xmlInputFactory(); _xmlOutputFactory = b.xmlOutputFactory(); _nameProcessor = b.xmlNameProcessor(); @@ -237,7 +249,7 @@ protected Object readResolve() { throw new IllegalArgumentException(e); } return new XmlFactory(_objectCodec, _xmlParserFeatures, _xmlGeneratorFeatures, - inf, outf, _cfgNameForTextElement); + inf, outf, _cfgNameForTextElement, _cfgValueForEmptyElement); } /** @@ -281,6 +293,20 @@ public void setXMLTextElementName(String name) { public String getXMLTextElementName() { return _cfgNameForTextElement; } + + /** + * @since 2.17 + */ + public void setEmptyElementValue(String value) { + _cfgValueForEmptyElement = value; + } + + /** + * @since 2.17 + */ + public String getEmptyElementValue() { + return _cfgValueForEmptyElement; + } /* /********************************************************** @@ -560,7 +586,7 @@ public FromXmlParser createParser(XMLStreamReader sr) throws IOException // false -> not managed FromXmlParser xp = new FromXmlParser(_createContext(_createContentReference(sr), false), - _parserFeatures, _xmlParserFeatures, _objectCodec, sr, _nameProcessor); + _parserFeatures, _xmlParserFeatures, _objectCodec, sr, _nameProcessor, _cfgValueForEmptyElement); if (_cfgNameForTextElement != null) { xp.setXMLTextElementName(_cfgNameForTextElement); } @@ -599,7 +625,7 @@ protected FromXmlParser _createParser(InputStream in, IOContext ctxt) throws IOE } sr = _initializeXmlReader(sr); FromXmlParser xp = new FromXmlParser(ctxt, _parserFeatures, _xmlParserFeatures, - _objectCodec, sr, _nameProcessor); + _objectCodec, sr, _nameProcessor, _cfgValueForEmptyElement); if (_cfgNameForTextElement != null) { xp.setXMLTextElementName(_cfgNameForTextElement); } @@ -617,7 +643,7 @@ protected FromXmlParser _createParser(Reader r, IOContext ctxt) throws IOExcepti } sr = _initializeXmlReader(sr); FromXmlParser xp = new FromXmlParser(ctxt, _parserFeatures, _xmlParserFeatures, - _objectCodec, sr, _nameProcessor); + _objectCodec, sr, _nameProcessor, _cfgValueForEmptyElement); if (_cfgNameForTextElement != null) { xp.setXMLTextElementName(_cfgNameForTextElement); } @@ -644,7 +670,7 @@ protected FromXmlParser _createParser(char[] data, int offset, int len, IOContex } sr = _initializeXmlReader(sr); FromXmlParser xp = new FromXmlParser(ctxt, _parserFeatures, _xmlParserFeatures, - _objectCodec, sr, _nameProcessor); + _objectCodec, sr, _nameProcessor, _cfgValueForEmptyElement); if (_cfgNameForTextElement != null) { xp.setXMLTextElementName(_cfgNameForTextElement); } @@ -678,7 +704,7 @@ protected FromXmlParser _createParser(byte[] data, int offset, int len, IOContex } sr = _initializeXmlReader(sr); FromXmlParser xp = new FromXmlParser(ctxt, _parserFeatures, _xmlParserFeatures, - _objectCodec, sr, _nameProcessor); + _objectCodec, sr, _nameProcessor, _cfgValueForEmptyElement); if (_cfgNameForTextElement != null) { xp.setXMLTextElementName(_cfgNameForTextElement); } diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactoryBuilder.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactoryBuilder.java index 117bbb90b..b80fe66f7 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactoryBuilder.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactoryBuilder.java @@ -54,6 +54,14 @@ public class XmlFactoryBuilder extends TSFBuilder */ protected String _nameForTextElement; + /** + * Set a default value in case of empty an empty element (empty XML tag) + *

+ * Value used for pseudo-property used for returning empty XML tag. + * Defaults to empty String, but may be changed. + */ + protected String _valueForEmptyElement = FromXmlParser.DEFAULT_EMPTY_ELEMENT_VALUE; + /** * Optional {@link ClassLoader} to use for constructing * {@link XMLInputFactory} and {@kink XMLOutputFactory} instances if @@ -91,6 +99,7 @@ public XmlFactoryBuilder(XmlFactory base) { _xmlInputFactory = base._xmlInputFactory; _xmlOutputFactory = base._xmlOutputFactory; _nameForTextElement = base._cfgNameForTextElement; + _valueForEmptyElement = base._cfgValueForEmptyElement; _nameProcessor = base._nameProcessor; _classLoaderForStax = null; } @@ -102,6 +111,8 @@ public XmlFactoryBuilder(XmlFactory base) { public String nameForTextElement() { return _nameForTextElement; } + public String valueForEmptyElement() { return _valueForEmptyElement; } + public XMLInputFactory xmlInputFactory() { if (_xmlInputFactory == null) { return defaultInputFactory(); @@ -213,6 +224,11 @@ public XmlFactoryBuilder nameForTextElement(String name) { return _this(); } + public XmlFactoryBuilder valueForEmptyElement(String value) { + _valueForEmptyElement = value; + return _this(); + } + /** * @since 2.13 (was misnamed as {@code inputFactory(in) formerly}) */ diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java index 0fc5c1e91..eb8c27598 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java @@ -104,6 +104,11 @@ public Builder nameForTextElement(String name) { return this; } + public Builder valueForEmptyElement(String value) { + _mapper.setValueForEmptyElement(value); + return this; + } + public Builder defaultUseWrapper(boolean state) { _mapper.setDefaultUseWrapper(state); return this; @@ -271,6 +276,10 @@ protected void setXMLTextElementName(String name) { getFactory().setXMLTextElementName(name); } + protected void setValueForEmptyElement(String value) { + getFactory().setEmptyElementValue(value); + } + /** * Since 2.7 * diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java index faabc22ce..ef9028733 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java @@ -40,6 +40,8 @@ public class FromXmlParser */ public final static String DEFAULT_UNNAMED_TEXT_PROPERTY = ""; + public final static String DEFAULT_EMPTY_ELEMENT_VALUE = ""; + /** * XML format has some peculiarities, indicated via new (2.12) capability * system. @@ -86,19 +88,6 @@ public enum Feature implements FormatFeature */ EMPTY_ELEMENT_AS_NULL(false), - /** - * Feature that indicates whether XML Empty elements (ones where there are - * no separate start and end tags, but just one tag that ends with "/>") - * are exposed as {@link JsonToken#START_ARRAY} {@link JsonToken#END_ARRAY}) or not. If they are not - * returned as `[]` tokens, they will be returned as {@link JsonToken#VALUE_STRING} - * tokens with textual value of "" (empty String). - *

- * Default setting is {@code false} - * - * @since 2.9 - */ - EMPTY_ELEMENT_AS_EMPTY_ARRAY(false), - /** * Feature that indicates whether XML Schema Instance attribute * {@code xsi:nil} will be processed automatically -- to indicate {@code null} @@ -172,6 +161,8 @@ private Feature(boolean defaultState) { */ protected String _cfgNameForTextElement = DEFAULT_UNNAMED_TEXT_PROPERTY; + protected String _cfgValueForEmptyElement = DEFAULT_EMPTY_ELEMENT_VALUE; + /* /********************************************************** /* Configuration @@ -289,17 +280,25 @@ private Feature(boolean defaultState) { */ public FromXmlParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures, - ObjectCodec codec, XMLStreamReader xmlReader, XmlNameProcessor tagProcessor) + ObjectCodec codec, XMLStreamReader xmlReader, XmlNameProcessor tagProcessor) + throws IOException + { + this(ctxt, genericParserFeatures, xmlFeatures, codec, xmlReader, tagProcessor, FromXmlParser.DEFAULT_EMPTY_ELEMENT_VALUE); + } + + public FromXmlParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures, + ObjectCodec codec, XMLStreamReader xmlReader, XmlNameProcessor tagProcessor, String valueForEmptyElement) throws IOException { super(genericParserFeatures); + _cfgValueForEmptyElement = valueForEmptyElement; _formatFeatures = xmlFeatures; _ioContext = ctxt; _streamReadConstraints = ctxt.streamReadConstraints(); _objectCodec = codec; _parsingContext = XmlReadContext.createRootContext(-1, -1); _xmlTokens = new XmlTokenStream(xmlReader, ctxt.contentReference(), - _formatFeatures, tagProcessor); + _formatFeatures, _cfgValueForEmptyElement, tagProcessor); final int firstToken; try { @@ -358,6 +357,13 @@ public void setXMLTextElementName(String name) { _cfgNameForTextElement = name; } + /** + * @since 2.17 + */ + public void setEmptyElementValue(String value) { + _cfgValueForEmptyElement = value; + } + /* /********************************************************************** /* Overrides: capability introspection methods diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java index 56e5fd296..2f3d9bd93 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java @@ -5,7 +5,6 @@ import javax.xml.XMLConstants; import javax.xml.stream.*; -import com.fasterxml.jackson.core.JsonToken; import org.codehaus.stax2.XMLStreamLocation2; import org.codehaus.stax2.XMLStreamReader2; @@ -121,6 +120,8 @@ public class XmlTokenStream */ protected String _textValue; + protected String _valueForEmptyElement; + /** * Marker flag set if caller wants to "push back" current token so * that next call to {@link #next()} should simply be given what was @@ -169,6 +170,12 @@ public class XmlTokenStream public XmlTokenStream(XMLStreamReader xmlReader, ContentReference sourceRef, int formatFeatures, XmlNameProcessor nameProcessor) + { + this(xmlReader, sourceRef, formatFeatures, FromXmlParser.DEFAULT_EMPTY_ELEMENT_VALUE, nameProcessor); + } + + public XmlTokenStream(XMLStreamReader xmlReader, ContentReference sourceRef, + int formatFeatures, String valueForEmptyElement, XmlNameProcessor nameProcessor) { _sourceReference = sourceRef; _formatFeatures = formatFeatures; @@ -177,6 +184,7 @@ public XmlTokenStream(XMLStreamReader xmlReader, ContentReference sourceRef, // 04-Dec-2023, tatu: [dataformat-xml#618] Need further customized adapter: _xmlReader = Stax2JacksonReaderAdapter.wrapIfNecessary(xmlReader); _nameProcessor = nameProcessor; + _valueForEmptyElement = valueForEmptyElement; } /** @@ -561,10 +569,7 @@ private final String _collectUntilTag() throws XMLStreamException if (FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL.enabledIn(_formatFeatures)) { return null; } - if (FromXmlParser.Feature.EMPTY_ELEMENT_AS_EMPTY_ARRAY.enabledIn(_formatFeatures)) { - return JsonToken.START_ARRAY.asString() + JsonToken.END_ARRAY.asString(); - } - return ""; + return _valueForEmptyElement; } CharSequence chars = null; diff --git a/src/test/java/com/fasterxml/jackson/dataformat/xml/deser/EmptyStringValueTest.java b/src/test/java/com/fasterxml/jackson/dataformat/xml/deser/EmptyStringValueTest.java index 509ac26e7..2f0865a5b 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/xml/deser/EmptyStringValueTest.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/xml/deser/EmptyStringValueTest.java @@ -97,7 +97,7 @@ public void testEmptyElementEmptyArray() throws Exception // but can be changed XmlMapper mapper2 = XmlMapper.builder() - .enable(FromXmlParser.Feature.EMPTY_ELEMENT_AS_EMPTY_ARRAY) + .valueForEmptyElement("[]") .build(); name = mapper2.readValue(XML, Name.class); assertNotNull(name); From eebd0989a523eb4a3f40cbb949ef0463ee839c7b Mon Sep 17 00:00:00 2001 From: Croway Date: Wed, 21 Feb 2024 10:47:27 +0100 Subject: [PATCH 3/4] Add javadoc --- .../jackson/dataformat/xml/XmlFactoryBuilder.java | 2 +- .../com/fasterxml/jackson/dataformat/xml/XmlMapper.java | 9 +++++++++ .../jackson/dataformat/xml/deser/FromXmlParser.java | 7 +++++++ .../jackson/dataformat/xml/deser/XmlTokenStream.java | 3 +-- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactoryBuilder.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactoryBuilder.java index b80fe66f7..70c2f2d2d 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactoryBuilder.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactoryBuilder.java @@ -55,7 +55,7 @@ public class XmlFactoryBuilder extends TSFBuilder protected String _nameForTextElement; /** - * Set a default value in case of empty an empty element (empty XML tag) + * Set a default value in case of an empty element (empty XML tag) *

* Value used for pseudo-property used for returning empty XML tag. * Defaults to empty String, but may be changed. diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java index eb8c27598..ce61afa40 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java @@ -104,6 +104,15 @@ public Builder nameForTextElement(String name) { return this; } + /** + * + * Set a default value in case of an empty element (empty XML tag) + *

+ * In case of an empty XML tag (like ``) the serialized value + * is set to `String value`. If not specified, the default value is empty String. + * + * @since 2.17 + */ public Builder valueForEmptyElement(String value) { _mapper.setValueForEmptyElement(value); return this; diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java index ef9028733..aa0b44a82 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java @@ -40,6 +40,10 @@ public class FromXmlParser */ public final static String DEFAULT_UNNAMED_TEXT_PROPERTY = ""; + /** + * The default value placeholder for XML empty tag is an empty + * String (""). + */ public final static String DEFAULT_EMPTY_ELEMENT_VALUE = ""; /** @@ -286,6 +290,9 @@ public FromXmlParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures, this(ctxt, genericParserFeatures, xmlFeatures, codec, xmlReader, tagProcessor, FromXmlParser.DEFAULT_EMPTY_ELEMENT_VALUE); } + /** + * @since 2.17 + */ public FromXmlParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures, ObjectCodec codec, XMLStreamReader xmlReader, XmlNameProcessor tagProcessor, String valueForEmptyElement) throws IOException diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java index 2f3d9bd93..ade87c271 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java @@ -120,7 +120,7 @@ public class XmlTokenStream */ protected String _textValue; - protected String _valueForEmptyElement; + protected final String _valueForEmptyElement; /** * Marker flag set if caller wants to "push back" current token so @@ -558,7 +558,6 @@ private final int _next() throws XMLStreamException /** * @return Collected text, if any, EXCEPT that if {@code FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL} - * OR {@code FromXmlParser.Feature.EMPTY_ELEMENT_AS_EMPTY_ARRAY} * AND empty element, returns {@code null} */ private final String _collectUntilTag() throws XMLStreamException From daf6dfaa1f7407dc7159fe22ce304e5a4e300938 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 1 Mar 2024 21:02:23 -0800 Subject: [PATCH 4/4] Minor cleanup; add javadocs, deprecation markers --- .../jackson/dataformat/xml/XmlFactory.java | 10 +++++-- .../jackson/dataformat/xml/XmlMapper.java | 5 ++++ .../dataformat/xml/deser/FromXmlParser.java | 30 +++++++++++-------- .../dataformat/xml/deser/XmlTokenStream.java | 7 ++++- .../xml/stream/XmlTokenStreamTest.java | 3 +- 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java index 22b93af27..9749698b5 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java @@ -66,6 +66,9 @@ public class XmlFactory extends JsonFactory protected String _cfgNameForTextElement; + /** + * @since 2.17 + */ protected String _cfgValueForEmptyElement; protected XmlNameProcessor _nameProcessor; @@ -112,6 +115,9 @@ public XmlFactory(ObjectCodec oc, int xpFeatures, int xgFeatures, this(oc, xpFeatures, xgFeatures, xmlIn, xmlOut, nameForTextElem, FromXmlParser.DEFAULT_EMPTY_ELEMENT_VALUE, XmlNameProcessors.newPassthroughProcessor()); } + /** + * @since 2.17 + */ public XmlFactory(ObjectCodec oc, int xpFeatures, int xgFeatures, XMLInputFactory xmlIn, XMLOutputFactory xmlOut, String nameForTextElem, String valueForEmptyElement) { @@ -248,8 +254,8 @@ protected Object readResolve() { } catch (Exception e) { throw new IllegalArgumentException(e); } - return new XmlFactory(_objectCodec, _xmlParserFeatures, _xmlGeneratorFeatures, - inf, outf, _cfgNameForTextElement, _cfgValueForEmptyElement); + return new XmlFactory(_objectCodec, _xmlParserFeatures, _xmlGeneratorFeatures, + inf, outf, _cfgNameForTextElement, _cfgValueForEmptyElement); } /** diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java index ce61afa40..60755dea4 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java @@ -285,6 +285,11 @@ protected void setXMLTextElementName(String name) { getFactory().setXMLTextElementName(name); } + // Needed by Builder itself in 2.x, but should not be called by users hence: + /** + * @deprecated Since 2.17 use {@link Builder#valueForEmptyElement(String)} instead + */ + @Deprecated protected void setValueForEmptyElement(String value) { getFactory().setEmptyElementValue(value); } diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java index aa0b44a82..1a7ca5038 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java @@ -165,7 +165,14 @@ private Feature(boolean defaultState) { */ protected String _cfgNameForTextElement = DEFAULT_UNNAMED_TEXT_PROPERTY; - protected String _cfgValueForEmptyElement = DEFAULT_EMPTY_ELEMENT_VALUE; + /** + * When an empty element (like {@code }) is encountered, this + * textual value reported in the token stream: default is empty + * String, but may be configured for any other String value. + * + * @since 2.17 + */ + protected final String _cfgValueForEmptyElement; /* /********************************************************** @@ -283,18 +290,24 @@ private Feature(boolean defaultState) { /********************************************************** */ + /** + * @deprecated Since 2.17 + */ + @Deprecated public FromXmlParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures, - ObjectCodec codec, XMLStreamReader xmlReader, XmlNameProcessor tagProcessor) - throws IOException + ObjectCodec codec, XMLStreamReader xmlReader, XmlNameProcessor tagProcessor) + throws IOException { - this(ctxt, genericParserFeatures, xmlFeatures, codec, xmlReader, tagProcessor, FromXmlParser.DEFAULT_EMPTY_ELEMENT_VALUE); + this(ctxt, genericParserFeatures, xmlFeatures, codec, xmlReader, tagProcessor, + FromXmlParser.DEFAULT_EMPTY_ELEMENT_VALUE); } /** * @since 2.17 */ public FromXmlParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures, - ObjectCodec codec, XMLStreamReader xmlReader, XmlNameProcessor tagProcessor, String valueForEmptyElement) + ObjectCodec codec, XMLStreamReader xmlReader, XmlNameProcessor tagProcessor, + String valueForEmptyElement) throws IOException { super(genericParserFeatures); @@ -364,13 +377,6 @@ public void setXMLTextElementName(String name) { _cfgNameForTextElement = name; } - /** - * @since 2.17 - */ - public void setEmptyElementValue(String value) { - _cfgValueForEmptyElement = value; - } - /* /********************************************************************** /* Overrides: capability introspection methods diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java index ade87c271..b45f52297 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java @@ -168,12 +168,17 @@ public class XmlTokenStream /********************************************************************** */ + @Deprecated // since 2.17 public XmlTokenStream(XMLStreamReader xmlReader, ContentReference sourceRef, int formatFeatures, XmlNameProcessor nameProcessor) { - this(xmlReader, sourceRef, formatFeatures, FromXmlParser.DEFAULT_EMPTY_ELEMENT_VALUE, nameProcessor); + this(xmlReader, sourceRef, formatFeatures, FromXmlParser.DEFAULT_EMPTY_ELEMENT_VALUE, + nameProcessor); } + /** + * @since 2.17 + */ public XmlTokenStream(XMLStreamReader xmlReader, ContentReference sourceRef, int formatFeatures, String valueForEmptyElement, XmlNameProcessor nameProcessor) { diff --git a/src/test/java/com/fasterxml/jackson/dataformat/xml/stream/XmlTokenStreamTest.java b/src/test/java/com/fasterxml/jackson/dataformat/xml/stream/XmlTokenStreamTest.java index 2936ef061..51d2aafd9 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/xml/stream/XmlTokenStreamTest.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/xml/stream/XmlTokenStreamTest.java @@ -181,7 +181,8 @@ private XmlTokenStream _tokensFor(String doc, int flags) throws Exception XMLStreamReader sr = XML_FACTORY.getXMLInputFactory().createXMLStreamReader(new StringReader(doc)); // must point to START_ELEMENT, so: sr.nextTag(); - XmlTokenStream stream = new XmlTokenStream(sr, ContentReference.rawReference(doc), flags, XmlNameProcessors.newPassthroughProcessor()); + XmlTokenStream stream = new XmlTokenStream(sr, ContentReference.rawReference(doc), flags, + "", XmlNameProcessors.newPassthroughProcessor()); stream.initialize(); return stream; }