From 3931e453d37ccac37554efc5067553f323228151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=95=E7=BA=A2=E5=AE=87?= Date: Tue, 15 Jul 2025 12:03:33 +0800 Subject: [PATCH 1/5] [issue 768]FromXmlParser lacks extension point for custom XmlTokenStream --- .../dataformat/xml/deser/FromXmlParser.java | 17 ++++++++++------- .../dataformat/xml/deser/XmlTokenStream.java | 4 ++++ 2 files changed, 14 insertions(+), 7 deletions(-) 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 ccdf5889..a56e1044 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 @@ -274,17 +274,20 @@ private Feature(boolean defaultState) { */ 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, codec, new XmlTokenStream(xmlReader, ctxt.contentReference(), xmlFeatures, tagProcessor)); + } + + public FromXmlParser(IOContext ctxt, int genericParserFeatures, ObjectCodec codec, XmlTokenStream xmlTokenStream) throws IOException { super(genericParserFeatures, ctxt.streamReadConstraints()); - _formatFeatures = xmlFeatures; _ioContext = ctxt; _objectCodec = codec; _parsingContext = XmlReadContext.createRootContext(-1, -1); - _xmlTokens = new XmlTokenStream(xmlReader, ctxt.contentReference(), - _formatFeatures, tagProcessor); - + if (xmlTokenStream == null) { + throw new IllegalArgumentException("xmlTokenStream cannot be null"); + } + _xmlTokens = xmlTokenStream; + _formatFeatures = xmlTokenStream.getFormatFeatures(); final int firstToken; try { firstToken = _xmlTokens.initialize(); 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 2a7c78be..1eba83b7 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 @@ -254,6 +254,10 @@ protected void setFormatFeatures(int f) { _cfgProcessXsiType = FromXmlParser.Feature.AUTO_DETECT_XSI_TYPE.enabledIn(f); } + public int getFormatFeatures() { + return _formatFeatures; + } + /* /********************************************************************** /* Public API From e117c3d5753a5f9d0f452daf8b285780ff009295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=95=E7=BA=A2=E5=AE=87?= Date: Wed, 16 Jul 2025 08:54:53 +0800 Subject: [PATCH 2/5] [issue 768]FromXmlParser lacks extension point for custom XmlTokenStream --- .../dataformat/xml/deser/FromXmlParser.java | 18 ++++++++++++++---- .../dataformat/xml/deser/XmlTokenStream.java | 6 ++++++ 2 files changed, 20 insertions(+), 4 deletions(-) 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 a56e1044..7fb2c00e 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 @@ -5,6 +5,7 @@ import java.io.Writer; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Objects; import java.util.Set; import javax.xml.XMLConstants; @@ -278,15 +279,24 @@ public FromXmlParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures, this(ctxt, genericParserFeatures, codec, new XmlTokenStream(xmlReader, ctxt.contentReference(), xmlFeatures, tagProcessor)); } + /** + * Constructs a new {@link FromXmlParser} instance using the provided XML token stream. + * This constructor initializes the parser with the given I/O context, parser features, + * and object codec for deserializing XML content into Java objects. + * + * @since 2.20 + * @param ctxt I/O context used for handling low-level I/O operations and buffering + * @param genericParserFeatures set of bitmasked parser features to control parsing behavior + * @param codec object codec used for converting between JSON-like structures and Java objects + * @param xmlTokenStream the pre-processed XML token stream to parse from + * @throws IOException if an I/O error occurs during initialization or parsing setup + */ public FromXmlParser(IOContext ctxt, int genericParserFeatures, ObjectCodec codec, XmlTokenStream xmlTokenStream) throws IOException { super(genericParserFeatures, ctxt.streamReadConstraints()); _ioContext = ctxt; _objectCodec = codec; _parsingContext = XmlReadContext.createRootContext(-1, -1); - if (xmlTokenStream == null) { - throw new IllegalArgumentException("xmlTokenStream cannot be null"); - } - _xmlTokens = xmlTokenStream; + _xmlTokens = Objects.requireNonNull(xmlTokenStream, "xmlTokenStream cannot be null"); _formatFeatures = xmlTokenStream.getFormatFeatures(); final int firstToken; try { 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 1eba83b7..af3dfb0a 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 @@ -254,6 +254,12 @@ protected void setFormatFeatures(int f) { _cfgProcessXsiType = FromXmlParser.Feature.AUTO_DETECT_XSI_TYPE.enabledIn(f); } + /** + * get FormatFeatures + * + * @return data + * @since 2.20 + */ public int getFormatFeatures() { return _formatFeatures; } From 51585ba51cc6c2aa4cbf0e618fceff1ba4c8b127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=95=E7=BA=A2=E5=AE=87?= Date: Wed, 16 Jul 2025 09:28:14 +0800 Subject: [PATCH 3/5] By abstracting the createParser method, developers no longer need to override four _createParser methods in XmlFactory when implementing their own custom FromXmlParser. This change is purely structural and does not involve any modification to the underlying logic of the code. --- .../jackson/dataformat/xml/XmlFactory.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 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 521c9bde..08a4d24b 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java @@ -559,7 +559,7 @@ public FromXmlParser createParser(XMLStreamReader sr) throws IOException } // false -> not managed - FromXmlParser xp = new FromXmlParser(_createContext(_createContentReference(sr), false), + FromXmlParser xp = this.createParser(_createContext(_createContentReference(sr), false), _parserFeatures, _xmlParserFeatures, _objectCodec, sr, _nameProcessor); if (_cfgNameForTextElement != null) { xp.setXMLTextElementName(_cfgNameForTextElement); @@ -567,6 +567,17 @@ public FromXmlParser createParser(XMLStreamReader sr) throws IOException return xp; } + /** + * Creates and returns a new instance of {@link FromXmlParser} configured with the provided parameters. + * If you need to extend or customize the FromXmlParser, you can simply override this method. + * + * @since 2.20 + */ + protected FromXmlParser createParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures, ObjectCodec codec, + XMLStreamReader xmlReader, XmlNameProcessor tagProcessor) throws IOException { + return new FromXmlParser(ctxt, genericParserFeatures, xmlFeatures, codec, xmlReader, tagProcessor); + } + /** * Factory method that wraps given {@link XMLStreamWriter}, usually to allow * incremental serialization to compose large output by serializing a sequence @@ -598,7 +609,7 @@ protected FromXmlParser _createParser(InputStream in, IOContext ctxt) throws IOE return StaxUtil.throwAsParseException(e, null); } sr = _initializeXmlReader(sr); - FromXmlParser xp = new FromXmlParser(ctxt, _parserFeatures, _xmlParserFeatures, + FromXmlParser xp = this.createParser(ctxt, _parserFeatures, _xmlParserFeatures, _objectCodec, sr, _nameProcessor); if (_cfgNameForTextElement != null) { xp.setXMLTextElementName(_cfgNameForTextElement); @@ -616,7 +627,7 @@ protected FromXmlParser _createParser(Reader r, IOContext ctxt) throws IOExcepti return StaxUtil.throwAsParseException(e, null); } sr = _initializeXmlReader(sr); - FromXmlParser xp = new FromXmlParser(ctxt, _parserFeatures, _xmlParserFeatures, + FromXmlParser xp = this.createParser(ctxt, _parserFeatures, _xmlParserFeatures, _objectCodec, sr, _nameProcessor); if (_cfgNameForTextElement != null) { xp.setXMLTextElementName(_cfgNameForTextElement); @@ -643,7 +654,7 @@ protected FromXmlParser _createParser(char[] data, int offset, int len, IOContex return StaxUtil.throwAsParseException(e, null); } sr = _initializeXmlReader(sr); - FromXmlParser xp = new FromXmlParser(ctxt, _parserFeatures, _xmlParserFeatures, + FromXmlParser xp = this.createParser(ctxt, _parserFeatures, _xmlParserFeatures, _objectCodec, sr, _nameProcessor); if (_cfgNameForTextElement != null) { xp.setXMLTextElementName(_cfgNameForTextElement); @@ -677,7 +688,7 @@ protected FromXmlParser _createParser(byte[] data, int offset, int len, IOContex return StaxUtil.throwAsParseException(e, null); } sr = _initializeXmlReader(sr); - FromXmlParser xp = new FromXmlParser(ctxt, _parserFeatures, _xmlParserFeatures, + FromXmlParser xp = this.createParser(ctxt, _parserFeatures, _xmlParserFeatures, _objectCodec, sr, _nameProcessor); if (_cfgNameForTextElement != null) { xp.setXMLTextElementName(_cfgNameForTextElement); From 7fabd87d372b9cdbacee413e43a0193cd1dcf5f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=95=E7=BA=A2=E5=AE=87?= Date: Wed, 16 Jul 2025 09:39:57 +0800 Subject: [PATCH 4/5] Abstract a createGenerator method to make it easier to extend. This change is purely structural and does not involve any modification to the underlying code logic. --- .../jackson/dataformat/xml/XmlFactory.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 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 08a4d24b..19cccbce 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java @@ -502,6 +502,16 @@ public JsonParser createParser(String content) throws IOException { /********************************************************** */ + /** + * Overriding this method makes it easy to extend and customize the ToXmlGenerator. + * + * @since 2.20 + */ + public ToXmlGenerator createGenerator(IOContext ctxt, int stdFeatures, int xmlFeatures, ObjectCodec codec, + XMLStreamWriter sw, XmlNameProcessor nameProcessor) { + return new ToXmlGenerator(ctxt, stdFeatures, xmlFeatures, codec, sw, nameProcessor); + } + @Override public ToXmlGenerator createGenerator(OutputStream out) throws IOException { return createGenerator(out, JsonEncoding.UTF8); @@ -513,7 +523,7 @@ public ToXmlGenerator createGenerator(OutputStream out, JsonEncoding enc) throws // false -> we won't manage the stream unless explicitly directed to final IOContext ctxt = _createContext(_createContentReference(out), false); ctxt.setEncoding(enc); - return new ToXmlGenerator(ctxt, + return this.createGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, _createXmlWriter(ctxt, out), _nameProcessor); } @@ -522,7 +532,7 @@ public ToXmlGenerator createGenerator(OutputStream out, JsonEncoding enc) throws public ToXmlGenerator createGenerator(Writer out) throws IOException { final IOContext ctxt = _createContext(_createContentReference(out), false); - return new ToXmlGenerator(ctxt, + return this.createGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, _createXmlWriter(ctxt, out), _nameProcessor); } @@ -535,7 +545,7 @@ public ToXmlGenerator createGenerator(File f, JsonEncoding enc) throws IOExcepti // true -> yes, we have to manage the stream since we created it final IOContext ctxt = _createContext(_createContentReference(out), true); ctxt.setEncoding(enc); - return new ToXmlGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, + return this.createGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, _createXmlWriter(ctxt, out), _nameProcessor); } @@ -589,7 +599,7 @@ public ToXmlGenerator createGenerator(XMLStreamWriter sw) throws IOException { sw = _initializeXmlWriter(sw); IOContext ctxt = _createContext(_createContentReference(sw), false); - return new ToXmlGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, + return this.createGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, sw, _nameProcessor); } From 43349a23fcf2512fed249d503193423d06a42247 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 15 Jul 2025 21:57:24 -0700 Subject: [PATCH 5/5] Add release notes --- release-notes/CREDITS-2.x | 5 +++ release-notes/VERSION-2.x | 2 + .../jackson/dataformat/xml/XmlFactory.java | 39 ++++++++++--------- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 50378fab..ec106be6 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -276,3 +276,8 @@ Bas Passon (@bpasson) * Contributed #745: Add feature to include `standalone='yes'` in xml declaration (2.19.0) + +(@xzxiaoshan) +* Contributed #768: `FromXmlParser` lacks extension point for passing + custom `XmlTokenStream` + (2.20.0) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 029235d5..dadc76fe 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -6,6 +6,8 @@ Project: jackson-dataformat-xml 2.20.0 (not yet released) +#768: `FromXmlParser` lacks extension point for passing custom `XmlTokenStream` + (contributed by @xzxiaoshan) - Generate SBOMs [JSTEP-14] 2.19.1 (13-Jun-2025) 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 19cccbce..cc6f2e83 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java @@ -38,16 +38,16 @@ public class XmlFactory extends JsonFactory public final static String FORMAT_NAME_XML = "XML"; /** - * Bitfield (set of flags) of all parser features that are enabled + * Bit field (set of flags) of all parser features that are enabled * by default. */ - final static int DEFAULT_XML_PARSER_FEATURE_FLAGS = FromXmlParser.Feature.collectDefaults(); + protected final static int DEFAULT_XML_PARSER_FEATURE_FLAGS = FromXmlParser.Feature.collectDefaults(); /** - * Bitfield (set of flags) of all generator features that are enabled + * Bit field (set of flags) of all generator features that are enabled * by default. */ - final static int DEFAULT_XML_GENERATOR_FEATURE_FLAGS = ToXmlGenerator.Feature.collectDefaults(); + protected final static int DEFAULT_XML_GENERATOR_FEATURE_FLAGS = ToXmlGenerator.Feature.collectDefaults(); /* /********************************************************** @@ -502,16 +502,6 @@ public JsonParser createParser(String content) throws IOException { /********************************************************** */ - /** - * Overriding this method makes it easy to extend and customize the ToXmlGenerator. - * - * @since 2.20 - */ - public ToXmlGenerator createGenerator(IOContext ctxt, int stdFeatures, int xmlFeatures, ObjectCodec codec, - XMLStreamWriter sw, XmlNameProcessor nameProcessor) { - return new ToXmlGenerator(ctxt, stdFeatures, xmlFeatures, codec, sw, nameProcessor); - } - @Override public ToXmlGenerator createGenerator(OutputStream out) throws IOException { return createGenerator(out, JsonEncoding.UTF8); @@ -523,7 +513,7 @@ public ToXmlGenerator createGenerator(OutputStream out, JsonEncoding enc) throws // false -> we won't manage the stream unless explicitly directed to final IOContext ctxt = _createContext(_createContentReference(out), false); ctxt.setEncoding(enc); - return this.createGenerator(ctxt, + return createGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, _createXmlWriter(ctxt, out), _nameProcessor); } @@ -532,7 +522,7 @@ public ToXmlGenerator createGenerator(OutputStream out, JsonEncoding enc) throws public ToXmlGenerator createGenerator(Writer out) throws IOException { final IOContext ctxt = _createContext(_createContentReference(out), false); - return this.createGenerator(ctxt, + return createGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, _createXmlWriter(ctxt, out), _nameProcessor); } @@ -545,7 +535,7 @@ public ToXmlGenerator createGenerator(File f, JsonEncoding enc) throws IOExcepti // true -> yes, we have to manage the stream since we created it final IOContext ctxt = _createContext(_createContentReference(out), true); ctxt.setEncoding(enc); - return this.createGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, + return createGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, _createXmlWriter(ctxt, out), _nameProcessor); } @@ -584,7 +574,7 @@ public FromXmlParser createParser(XMLStreamReader sr) throws IOException * @since 2.20 */ protected FromXmlParser createParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures, ObjectCodec codec, - XMLStreamReader xmlReader, XmlNameProcessor tagProcessor) throws IOException { + XMLStreamReader xmlReader, XmlNameProcessor tagProcessor) throws IOException { return new FromXmlParser(ctxt, genericParserFeatures, xmlFeatures, codec, xmlReader, tagProcessor); } @@ -599,10 +589,21 @@ public ToXmlGenerator createGenerator(XMLStreamWriter sw) throws IOException { sw = _initializeXmlWriter(sw); IOContext ctxt = _createContext(_createContentReference(sw), false); - return this.createGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, + return createGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, sw, _nameProcessor); } + /** + * Factory method called by more other {@code creatoGenerator} methods. + * Overriding this method makes it easy to extend and customize the ToXmlGenerator. + * + * @since 2.20 + */ + public ToXmlGenerator createGenerator(IOContext ctxt, int stdFeatures, int xmlFeatures, ObjectCodec codec, + XMLStreamWriter sw, XmlNameProcessor nameProcessor) { + return new ToXmlGenerator(ctxt, stdFeatures, xmlFeatures, codec, sw, nameProcessor); + } + /* /********************************************************** /* Internal factory method overrides