@@ -184,16 +184,48 @@ pub(crate) fn _escape<F: Fn(u8) -> bool>(raw: &str, escape_chars: F) -> Cow<str>
184184/// [`escape-html`]: ../index.html#escape-html
185185/// [HTML5 escapes]: https://dev.w3.org/html5/html-author/charref
186186pub fn unescape ( raw : & str ) -> Result < Cow < str > , EscapeError > {
187- unescape_with ( raw, |_| None )
187+ unescape_with ( raw, resolve_predefined_entity )
188188}
189189
190190/// Unescape an `&str` and replaces all xml escaped characters (`&...;`) into
191191/// their corresponding value, using a resolver function for custom entities.
192192///
193193/// If feature [`escape-html`] is enabled, then recognizes all [HTML5 escapes].
194194///
195+ /// Predefined entities will be resolved _after_ trying to resolve with `resolve_entity`,
196+ /// which allows you to override default behavior which required in some XML dialects.
197+ ///
198+ /// Character references (`&#hh;`) cannot be overridden, they are resolved before
199+ /// calling `resolve_entity`.
200+ ///
201+ /// Note, that entities will not be resolved recursively. In order to satisfy the
202+ /// XML [requirements] you should unescape nested entities by yourself.
203+ ///
204+ /// # Example
205+ ///
206+ /// ```
207+ /// use quick_xml::escape::resolve_xml_entity;
208+ /// # use quick_xml::escape::unescape_with;
209+ /// # use pretty_assertions::assert_eq;
210+ /// let override_named_entities = |entity: &str| match entity {
211+ /// // Override standard entities
212+ /// "lt" => Some("FOO"),
213+ /// "gt" => Some("BAR"),
214+ /// // Resolve custom entities
215+ /// "baz" => Some("<"),
216+ /// // Delegate other entities to the default implementation
217+ /// _ => resolve_xml_entity(entity),
218+ /// };
219+ ///
220+ /// assert_eq!(
221+ /// unescape_with("&<test>&baz;", override_named_entities).unwrap(),
222+ /// "&FOOtestBAR<"
223+ /// );
224+ /// ```
225+ ///
195226/// [`escape-html`]: ../index.html#escape-html
196227/// [HTML5 escapes]: https://dev.w3.org/html5/html-author/charref
228+ /// [requirements]: https://www.w3.org/TR/xml11/#intern-replacement
197229pub fn unescape_with < ' input , ' entity , F > (
198230 raw : & ' input str ,
199231 mut resolve_entity : F ,
@@ -221,8 +253,6 @@ where
221253 if let Some ( entity) = pat. strip_prefix ( '#' ) {
222254 let codepoint = parse_number ( entity, start..end) ?;
223255 unescaped. push_str ( codepoint. encode_utf8 ( & mut [ 0u8 ; 4 ] ) ) ;
224- } else if let Some ( value) = resolve_predefined_entity ( pat) {
225- unescaped. push_str ( value) ;
226256 } else if let Some ( value) = resolve_entity ( pat) {
227257 unescaped. push_str ( value) ;
228258 } else {
@@ -1840,10 +1870,7 @@ fn test_unescape_with() {
18401870 assert_eq ! ( unchanged, Cow :: Borrowed ( "test" ) ) ;
18411871 assert ! ( matches!( unchanged, Cow :: Borrowed ( _) ) ) ;
18421872
1843- assert_eq ! (
1844- unescape_with( "<test>" , custom_entities) . unwrap( ) ,
1845- "<test>"
1846- ) ;
1873+ assert ! ( unescape_with( "<" , custom_entities) . is_err( ) ) ;
18471874 assert_eq ! ( unescape_with( "0" , custom_entities) . unwrap( ) , "0" ) ;
18481875 assert_eq ! ( unescape_with( "0" , custom_entities) . unwrap( ) , "0" ) ;
18491876 assert_eq ! ( unescape_with( "&foo;" , custom_entities) . unwrap( ) , "BAR" ) ;
0 commit comments