Repository navigation
Releases
v0.42.0 - String Ergonomics
What's Changed
0.42.0 -- 2026-08-22
This is a large release. The primary change is an ergonomic improvement across the entire API -
quick_xml now makes use of &str and String types where possible instead of
&[u8] and Vec<u8>. This requires significant refactoring of downstream code,
but should result in a net simplification as well as potential performance improvements,
and opens up additional opportunities in future releases.
The MSRV has been raised to 1.86. We now use Rust 2024 Edition.
Breaking Changes
- #963: Reader now validates that input is valid UTF-8 when constructing events.
Non-UTF-8 input passed toReader::from_reader()withoutDecodingReaderwill now
produceError::Encodinginstead of silently passing through invalid bytes.
UseDecodingReaderto transcode non-UTF-8 sources. - #963: Name types (
QName,LocalName,Prefix,Namespace,PrefixDeclaration)
now wrap&strinstead of&[u8].into_inner()returns&str, andAsRef<str>
is implemented (AsRef<[u8]>has been removed).ResolveResult::Unknownnow containsString
instead ofVec<u8>, andNamespaceErrorvariants containStringinstead ofVec<u8>. - #963: Removed the
decoder: Decoderfield from event types (BytesStart,BytesText,
BytesCData,BytesRef) andAttributes. Thedecoder()method is no longer available
on these types. Decode methods on events now always assume UTF-8 input.
Error::missed_end()no longer takes aDecoderparameter. - #963: Event types (
BytesStart,BytesEnd,BytesText,BytesCData,BytesPI,
BytesRef) now storeCow<str>internally instead ofCow<[u8]>.into_inner()on
BytesText,BytesCData,BytesPI, andBytesRefnow returnsCow<str>.
BytesStart::set_name()now takes&strinstead of&[u8]. - #963: All event types and the
Eventenum now implementDeref<Target = str>
instead ofDeref<Target = [u8]>. ExplicitAsRef<str>impls are provided to
avoid ambiguity. - #963: Removed
decode()methods fromBytesText,BytesCData, andBytesRef.
Content is already available as&strviaDeref. Thexml10_content(),
xml11_content(),xml_content(), andhtml_content()methods now return
Cow<str>directly instead ofResult<Cow<str>, EncodingError>. - #963:
Attribute::valueis nowCow<'a, str>instead ofCow<'a, [u8]>.
TheFrom<(&[u8], &[u8])>impl has been removed. - #963:
BytesDecl::version(),encoding(), andstandalone()now return
Cow<'_, str>instead ofCow<'_, [u8]>. - #963: Removed
Reader::decoder()method. UseReader::encoding()instead
(available with theencodingfeature). Removeddecoder()from theXmlRead
serde trait. Removed all methods fromDecoder(the struct is kept only for
backward compatibility with deprecatedAttributemethods). - #980:
NamespaceError::TooManyDeclarationshas been renamed toTooManyBindings,
andNamespaceResolver::set_max_declarations_per_elementhas been renamed to
NamespaceResolver::set_max_namespace_bindings, and the semantic behavior has
changed slightly. The default maximum has also been reduced from 256 to 128. - #1000:
DeError::UnexpectedStartrenamed toDeError::MixedContent. That error
is emitted when you try to deserialize boolean, number or stringfieldfrom
something like<field>text <tag/> another text</field>.
Bug Fixes
- #670: Serde serializer now escapes
\r,\n, and\tin attribute values
as , , and	respectively, preventing silent data loss from
XML attribute-value normalization on round-trip. LikewiseAttribute::from
performs the same transformation. - #953: The serde
Deserializernow correctly handles namespaces. Previously
the namespace bindings might be applied or removed before the event actually
was consumed which lead to a couple of bugs. - #989:
Attributes::newandAttributes::htmlnow return empty iterators when
their starting position is past the end of the input instead of panicking. - #977:
NamespaceResolver::push(and hence everyNsReaderStart/Empty
event) now returns the newNamespaceError::TooDeeplyNestedwhen a document
nests elements deeper thanu16::MAX, instead of overflowing the internal
u16depth counter. Previously the unguardednesting_level += 1panicked
underoverflow-checksbuilds and silently wrapped in release, corrupting
namespace-scope bookkeeping on deeply nested untrusted input. - #980:
NamespaceResolvernow caps the total number of in-scope namespace
bindings (default 128, configurable viaset_max_namespace_bindings),
replacing the previous per-elementmax_declarations_per_elementlimit. - #978: The serde
Deserializernow enforces a configurable recursion-depth
limit (default 128, matchingserde_json). Deeply nested XML returns
DeError::TooDeeplyNestedinstead of overflowing the native call stack.
UseDeserializer::recursion_limit()to adjust. - #990:
\rin text content is now escaped as by the serde serializer,
BytesText::new(),escape(),partial_escape(), andminimal_escape(),
preventing silent conversion to\nfrom XML end-of-line normalization on
round-trip. Note that\rcannot be preserved through CDATA serialization
because character references are not permitted inside CDATA sections.
Misc Changes
- #269: Added getting-started examples (
getting_started,writer,
serde_roundtrip,reader_patterns,visitor) and anexamples/README.md
guide on choosing between the serde and pull-reader/writer APIs. - #331: Documentation about lifetimes of the events and attributes has been clarified.
- #859: Added an example showing how to pretty-print serialized XML.
- #983: Adopted an AI use and contribution policy for new upstream contributions.
- #963: MSRV bumped to 1.86 (April 2025)
- #963: Deprecated
Attributemethods that take aDecoderparameter, since
attribute values are now always valid UTF-8:decoded_and_normalized_value(),
decoded_and_normalized_value_with(),decode_and_unescape_value(), and
decode_and_unescape_value_with(). Usenormalized_value()and
normalized_value_with()instead. - #1002: Added
NamespaceResolver::withthat allows temporary applying namespace
bindings from the start tag for the scope of a provided closure F, without making any
persistent change to the resolver. It is useful to check a peeked event which is
not yet consumed in custom implementations of peekable reader. - #1002: Added
Deserializer::resolverandDeserializer::resolver_mutmethods
to get a namespace resolver used by this deserializer, because it no longer uses
anNsReaderinternally. - #1005: Implement
Hash,PartialOrd, andOrdforBytesTextandBytesCDatatypes.
New Contributors
- @maxtaran2010 made their first contribution in #976
- @scadastrangelove made their first contribution in #979
- @lntutor made their first contribution in #992
Full Changelog: v0.41.0...v0.42.0
v0.41.0 - Secuirity fixes
What's Changed
New Features
- #970: Add
NsReader::resolver_mut()andNamespaceResolver::{max_declarations_per_element, set_max_declarations_per_element}.
Bug Fixes
- #969:
Attributes(and anything that iteratesBytesStart::attributes()with the defaultwith_checks(true)) no longer takes O(NΒ²) time on a start tag with a large number of attributes. Small tags keep the previous linear scan; larger ones switch to a 64-bit hash pre-filter, so the whole tag is O(N). The exactAttrError::Duplicated(new, prev)positions are unchanged. - #970:
NamespaceResolver::push(and hence everyNsReaderStart/Emptyevent) now rejects a start tag that declares more thanDEFAULT_MAX_DECLARATIONS_PER_ELEMENT(256)xmlns/xmlns:*namespace bindings, returning the newNamespaceError::TooManyDeclarations. Previouslypushallocated oneNamespaceBindingper declaration with no upper bound, before the event was returned to the caller, so anNsReaderconsumer could not bound its memory exposure on untrusted input. The limit is configurable viaNamespaceResolver::set_max_declarations_per_element(useusize::MAXto disable).
New Contributors
- @qifan-sailboat made their first contribution in #972
Full Changelog: v0.40.1...v0.41.0
v0.40.1 - Fix rarely possible serde deserialization panic
What's Changed
- #964: Fix
unreachable!()panic in the serde deserializer when a DOCTYPE declaration appears between two text runs inside an element (e.g.<a>x<!DOCTYPE y>z</a>). The DOCTYPE used to breakdrain_text's consecutive-text merge, so twoDeEvent::Textevents reachedread_textand tripped its "Cannot be two consequent Text events" invariant. DOCTYPE is now treated as transparent during text drain β it still goes through the entity resolver, but the surrounding text is merged into one run. Discovered via libFuzzer on a real-world SAML deserializer harness.
New Contributors
- @williamareynolds made their first contribution in #964
Full Changelog: v0.40.0...v0.40.1
v0.40.0 - UTF-16 and ISO-2022-JP encodings supported
What's Changed
MSRV bumped to 1.79.
Now quick-xml supports the UTF-16 and ISO-2022-JP encoded documents. See the new DecodingReader type.
New Features
-
#956: Add
DecodingReader, aBufReadadapter that auto-detects encoding from BOM or XML declaration and transcodes to UTF-8. Enabled by theencodingfeature. -
#938: Add new enumeration
XmlVersionand typified getterBytesDecl::xml_version(). -
#938: Add new error variant
IllFormedError::UnknownVersion. -
#371: Add new error variant
EscapeError::TooManyNestedEntities. -
#371: Improved compliance with the XML attribute value normalization process by adding
Attribute::normalized_value()Attribute::normalized_value_with()Attribute::decoded_and_normalized_value()Attribute::decoded_and_normalized_value_with()
which ought to be used in place of deprecated
Attribute::unescape_value()Attribute::unescape_value_with()Attribute::decode_and_unescape_value()Attribute::decode_and_unescape_value_with()
Deprecated functions now behaves the same as newly added.
Bug Fixes
- #938: Use correct rules for EOL normalization in
Deserializerwhen parse XML 1.0 documents. Previously XML 1.1. rules was applied.
Misc Changes
- #914: Remove deprecated
.prefixes(),.resolve(),.resolve_attribute(), and.resolve_element()ofNsReader. Use.resolver().<...>methods instead. - #938: Now
BytesText::xml_content,BytesCData::xml_contentandBytesRef::xml_contentacceptsXmlVersionparameter to apply correct EOL normalization rules. - #944:
read_text()now returnsBytesTextwhich allows you to get the content with properly normalized EOLs. To get the previous behavior use.read_text().decode()?. - #956: Bumped MSRV from 1.59 (Feb 2022) to 1.79 (June 2024)
New Contributors
Full Changelog: v0.39.4...v0.40.0
v0.39.4 - Fix another panics when parse malformed DTD
Bug Fixes
- #957: Fix slice-index panic when reading malformed DTD whose unknown markup is split across
BufReaderchunks. As with #950, the returnedEvent::DocTypemay contain the malformed DTD; this fix only ensures that the parser does not panic. - #960: Fix sibling slice-index panic when a single chunk delivers
<followed by 9+ bytes of unknown markup inside a DTD internal subset. Same disposition as #957 / #950: parser must not panic; DTD validity reporting is a future improvement.
Full Changelog: v0.39.3...v0.39.4
v0.39.3 - Fix panic when parse malformed DTD
Bug Fixes
- #950: Fix subtraction with overflow when parse malformed DTD in some cases. Note, that currently we do not check the validity of DTD, so the returned
Event::DocTypemay contain the malformed DTD.
Full Changelog: v0.39.2...v0.39.3
v0.39.2 - Fix regression and read_text_into
What's Changed
New Features
- #483: Implement
read_text_into()andread_text_into_async().
Bug Fixes
- #939: Fix parsing error of the tag from buffered reader, when the first byte
<is the last in theBufReadinternal buffer. This is the regression from #936.
Full Changelog: v0.39.1...v0.39.2
v0.39.1 - Fixes in read_to_end / read_text
What's Changed
New Features
- #598: Add method
NamespaceResolver::set_levelwhich may be helpful in some circumstances.
Bug Fixes
- #597: Fix incorrect processing of namespace scopes in
NsReader::read_to_end,NsReader::read_to_end_into,NsReader::read_to_end_into_asyncandNsReader::read_text. The scope started by a start element was not ended after that call. - #936: Fix incorrect result of
.read_text()when it is called after readingTextorGeneralRefevent.
v0.39.0 - Config for Writer
What's Changed
Added a way to configure Writer. Now all configuration is contained in the writer::Config
struct and can be applied at once. When serde-types feature is enabled, configuration is serializable.
New Features
- #846: Add methods
config()andconfig_mut()to inspect and change the writer configuration. - #846: Add ability to write space before
/>in self-closed tags for maximum compatibility with XHTML. - #846: Add method
empty_element_handling()as a more powerful alternative toexpand_empty_elements()inSerializer. - #929: Allow to pass list of field names to
impl_deserialize_for_internally_tagged_enum!macro which is required if you enum variants contains$valuefields.
Bug Fixes
- #923: Implement correct skipping of well-formed DTD.
Misc Changes
- #908: Increase minimal supported
serdeversion from 1.0.139 to 1.0.180. - #913: Deprecate
.prefixes(),.resolve(),.resolve_attribute(), and.resolve_element()ofNsReader. Use.resolver().bindings()and.resolver().resolve()methods instead. - #913:
Attributes::has_nilnow acceptsNamespaceResolverinstead ofReader<R>. - #924: (breaking change) Split
SyntaxError::UnclosedPIOrXmlDeclintoUnclosedPIandUnclosedXmlDeclfor more precise error reporting. - #924: (breaking change)
Parser::eof_errornow takes&selfand content&[u8]parameters. - #926: (breaking change) Split
SyntaxError::UnclosedTagintoUnclosedTag,
UnclosedSingleQuotedAttributeValueandUnclosedDoubleQuotedAttributeValuefor more precise error reporting.
New Contributors
- @rzmk made their first contribution in #920
- @zrneely made their first contribution in #922
- @SuchAFuriousDeath made their first contribution in #924
- @tayu0110 made their first contribution in #925
Full Changelog: v0.38.4...v0.39.0
v0.38.4 - CDATA serialization in serde
What's Changed
New Features
- #353: Add ability to serialize textual content as CDATA sections in
Serializer. Everywhere where the text node may be created, a CDATA section(s) could be produced instead. See the newSerializer::text_format()method.
Bug Fixes
- #912: Fix deserialization of numbers, booleans and characters that is space-wrapped, for example
<int> 42 </int>. That space characters are usually indent added during serialization and other XML serialization libraries trims them
Misc Changes
New Contributors
- @Ninja3047 made their first contribution in #904
- @alexanderkjall made their first contribution in #901
Full Changelog: v0.38.3...v0.38.4