Skip to content

Commit c442a0c

Browse files
committed
Allow both &text and $value fields in the same struct
failures: fixed_name::variable_size::text_and_value variable_name::variable_size::text_and_value
1 parent 8258d97 commit c442a0c

File tree

4 files changed

+139
-11
lines changed

4 files changed

+139
-11
lines changed

Changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ XML specification. See the updated `custom_entities` example!
3333

3434
### Bug Fixes
3535

36+
- [#868]: Allow to have both `$text` and `$value` special fields in one struct. Previously
37+
any text will be recognized as `$value` field even when `$text` field is also presented.
38+
3639
### Misc Changes
3740

3841
- [#863]: Remove `From<QName<'a>> for BytesStart<'a>` because now `BytesStart` stores the
@@ -42,6 +45,7 @@ XML specification. See the updated `custom_entities` example!
4245

4346
[#766]: https://github.com/tafia/quick-xml/pull/766
4447
[#863]: https://github.com/tafia/quick-xml/pull/863
48+
[#868]: https://github.com/tafia/quick-xml/pull/868
4549
[general entity]: https://www.w3.org/TR/xml11/#gen-entity
4650

4751

src/de/map.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ where
192192
/// <tag>value for VALUE_KEY field<tag>
193193
/// ```
194194
has_value_field: bool,
195+
/// Determines if struct have [`VALUE_KEY`], but not [`TEXT_KEY`] special fields.
196+
text_as_value: bool,
195197
}
196198

197199
impl<'de, 'd, R, E> ElementMapAccess<'de, 'd, R, E>
@@ -205,13 +207,16 @@ where
205207
start: BytesStart<'de>,
206208
fields: &'static [&'static str],
207209
) -> Result<Self, DeError> {
210+
let has_value_field = fields.contains(&VALUE_KEY);
208211
Ok(Self {
209212
de,
210213
iter: IterState::new(start.name().as_ref().len(), false),
211214
start,
212215
source: ValueSource::Unknown,
213216
fields,
214-
has_value_field: fields.contains(&VALUE_KEY),
217+
has_value_field,
218+
// If we have dedicated "$text" field, it will not be passed to "$value" field
219+
text_as_value: has_value_field && !fields.contains(&TEXT_KEY),
215220
})
216221
}
217222

@@ -264,10 +269,7 @@ where
264269
} else {
265270
// try getting from events (<key>value</key>)
266271
match self.de.peek()? {
267-
// We shouldn't have both `$value` and `$text` fields in the same
268-
// struct, so if we have `$value` field, the we should deserialize
269-
// text content to `$value`
270-
DeEvent::Text(_) if self.has_value_field => {
272+
DeEvent::Text(_) if self.text_as_value => {
271273
self.source = ValueSource::Content;
272274
// Deserialize `key` from special attribute name which means
273275
// that value should be taken from the text content of the

src/de/mod.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,6 +1506,28 @@
15061506
//! The only difference is in how complex types and sequences are serialized.
15071507
//! If you doubt which one you should select, begin with [`$value`](#value).
15081508
//!
1509+
//! If you have both `$text` and `$value` in you struct, then text events will be
1510+
//! mapped to the `$text` field:
1511+
//!
1512+
//! ```
1513+
//! # use serde::Deserialize;
1514+
//! # use quick_xml::de::from_str;
1515+
//! #[derive(Deserialize, PartialEq, Debug)]
1516+
//! struct TextAndValue {
1517+
//! #[serde(rename = "$text")]
1518+
//! text: Option<String>,
1519+
//!
1520+
//! #[serde(rename = "$value")]
1521+
//! value: Option<String>,
1522+
//! }
1523+
//!
1524+
//! let object: TextAndValue = from_str("<AnyName>text <![CDATA[and CDATA]]></AnyName>").unwrap();
1525+
//! assert_eq!(object, TextAndValue {
1526+
//! text: Some("text and CDATA".to_string()),
1527+
//! value: None,
1528+
//! });
1529+
//! ```
1530+
//!
15091531
//! ## `$text`
15101532
//! `$text` is used when you want to write your XML as a text or a CDATA content.
15111533
//! More formally, field with that name represents simple type definition with
@@ -1744,12 +1766,6 @@
17441766
//! assert_eq!(object, obj);
17451767
//! ```
17461768
//!
1747-
//! ----------------------------------------------------------------------------
1748-
//!
1749-
//! You can have either `$text` or `$value` field in your structs. Unfortunately,
1750-
//! that is not enforced, so you can theoretically have both, but you should
1751-
//! avoid that.
1752-
//!
17531769
//!
17541770
//!
17551771
//! Frequently Used Patterns

tests/serde-de-seq.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,28 @@ mod fixed_name {
821821
);
822822
}
823823

824+
#[test]
825+
fn text_and_value() {
826+
#[derive(Debug, PartialEq, Deserialize)]
827+
struct List {
828+
#[serde(rename = "$text")]
829+
text: (),
830+
item: [(); 2],
831+
}
832+
833+
from_str::<List>(
834+
r#"
835+
<root>
836+
<item/>
837+
<item/>
838+
text
839+
<![CDATA[cdata]]>
840+
</root>
841+
"#,
842+
)
843+
.unwrap();
844+
}
845+
824846
/// Checks that sequences represented by elements can contain sequences,
825847
/// represented by [`xs:list`s](https://www.w3schools.com/xml/el_list.asp)
826848
mod xs_list {
@@ -1656,6 +1678,36 @@ mod fixed_name {
16561678
);
16571679
}
16581680

1681+
#[test]
1682+
fn text_and_value() {
1683+
#[derive(Debug, PartialEq, Deserialize)]
1684+
struct List {
1685+
#[serde(rename = "$text")]
1686+
text: (),
1687+
item: Vec<()>,
1688+
}
1689+
1690+
let data: List = from_str(
1691+
r#"
1692+
<root>
1693+
<item/>
1694+
<item/>
1695+
text
1696+
<![CDATA[cdata]]>
1697+
</root>
1698+
"#,
1699+
)
1700+
.unwrap();
1701+
1702+
assert_eq!(
1703+
data,
1704+
List {
1705+
text: (),
1706+
item: vec![(); 2],
1707+
}
1708+
);
1709+
}
1710+
16591711
/// Checks that sequences represented by elements can contain sequences,
16601712
/// represented by `xs:list`s
16611713
mod xs_list {
@@ -2883,6 +2935,29 @@ mod variable_name {
28832935
);
28842936
}
28852937

2938+
#[test]
2939+
fn text_and_value() {
2940+
#[derive(Debug, PartialEq, Deserialize)]
2941+
struct List {
2942+
#[serde(rename = "$text")]
2943+
text: (),
2944+
#[serde(rename = "$value")]
2945+
value: [(); 2],
2946+
}
2947+
2948+
from_str::<List>(
2949+
r#"
2950+
<root>
2951+
<item/>
2952+
<item/>
2953+
text
2954+
<![CDATA[cdata]]>
2955+
</root>
2956+
"#,
2957+
)
2958+
.unwrap();
2959+
}
2960+
28862961
/// Checks that sequences represented by elements can contain sequences,
28872962
/// represented by `xs:list`s
28882963
mod xs_list {
@@ -3929,6 +4004,37 @@ mod variable_name {
39294004
.unwrap_err();
39304005
}
39314006

4007+
#[test]
4008+
fn text_and_value() {
4009+
#[derive(Debug, PartialEq, Deserialize)]
4010+
struct List {
4011+
#[serde(rename = "$text")]
4012+
text: (),
4013+
#[serde(rename = "$value")]
4014+
value: Vec<()>,
4015+
}
4016+
4017+
let data: List = from_str(
4018+
r#"
4019+
<root>
4020+
<item/>
4021+
<item/>
4022+
text
4023+
<![CDATA[cdata]]>
4024+
</root>
4025+
"#,
4026+
)
4027+
.unwrap();
4028+
4029+
assert_eq!(
4030+
data,
4031+
List {
4032+
text: (),
4033+
value: vec![(); 2],
4034+
}
4035+
);
4036+
}
4037+
39324038
/// Checks that sequences represented by elements can contain sequences,
39334039
/// represented by `xs:list`s
39344040
mod xs_list {

0 commit comments

Comments
 (0)