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 521c9bde..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(); /* /********************************************************** @@ -513,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 new ToXmlGenerator(ctxt, + return createGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, _createXmlWriter(ctxt, out), _nameProcessor); } @@ -522,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 new ToXmlGenerator(ctxt, + return createGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, _createXmlWriter(ctxt, out), _nameProcessor); } @@ -535,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 new ToXmlGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, + return createGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, _createXmlWriter(ctxt, out), _nameProcessor); } @@ -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 @@ -578,10 +589,21 @@ public ToXmlGenerator createGenerator(XMLStreamWriter sw) throws IOException { sw = _initializeXmlWriter(sw); IOContext ctxt = _createContext(_createContentReference(sw), false); - return new ToXmlGenerator(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 @@ -598,7 +620,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 +638,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 +665,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 +699,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); 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..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; @@ -274,17 +275,29 @@ 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)); + } + + /** + * 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()); - _formatFeatures = xmlFeatures; _ioContext = ctxt; _objectCodec = codec; _parsingContext = XmlReadContext.createRootContext(-1, -1); - _xmlTokens = new XmlTokenStream(xmlReader, ctxt.contentReference(), - _formatFeatures, tagProcessor); - + _xmlTokens = Objects.requireNonNull(xmlTokenStream, "xmlTokenStream cannot be null"); + _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..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,16 @@ 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; + } + /* /********************************************************************** /* Public API