@@ -23,6 +23,15 @@ pub enum QuoteTarget {
23
23
SingleQAttr ,
24
24
}
25
25
26
+ /// Defines the format for text content serialization
27
+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
28
+ pub enum TextFormat {
29
+ /// Serialize as regular text content with escaping
30
+ Text ,
31
+ /// Serialize as CDATA section without escaping
32
+ CData ,
33
+ }
34
+
26
35
/// Escapes atomic value that could be part of a `xs:list`. All whitespace characters
27
36
/// additionally escaped
28
37
fn escape_item ( value : & str , target : QuoteTarget , level : QuoteLevel ) -> Cow < ' _ , str > {
@@ -93,6 +102,37 @@ fn escape_item(value: &str, target: QuoteTarget, level: QuoteLevel) -> Cow<'_, s
93
102
}
94
103
}
95
104
105
+ /// Writes content as CDATA section, handling the `]]>` sequence by splitting into multiple CDATA sections
106
+ fn write_cdata_content < W : Write > ( writer : & mut W , value : & str ) -> Result < ( ) , SeError > {
107
+ if value. is_empty ( ) {
108
+ writer. write_str ( "<![CDATA[]]>" ) ?;
109
+ return Ok ( ( ) ) ;
110
+ }
111
+
112
+ let mut remaining = value;
113
+
114
+ while !remaining. is_empty ( ) {
115
+ writer. write_str ( "<![CDATA[" ) ?;
116
+
117
+ // Find the first occurrence of "]]>"
118
+ if let Some ( pos) = remaining. find ( "]]>" ) {
119
+ // Write everything up to and including "]]"
120
+ writer. write_str ( & remaining[ ..pos + 2 ] ) ?;
121
+ writer. write_str ( "]]>" ) ?;
122
+
123
+ // Continue with ">" and the rest
124
+ remaining = & remaining[ pos + 2 ..] ;
125
+ } else {
126
+ // No "]]>" found, write the rest
127
+ writer. write_str ( remaining) ?;
128
+ writer. write_str ( "]]>" ) ?;
129
+ break ;
130
+ }
131
+ }
132
+
133
+ Ok ( ( ) )
134
+ }
135
+
96
136
/// Escapes XSD simple type value
97
137
fn escape_list ( value : & str , target : QuoteTarget , level : QuoteLevel ) -> Cow < ' _ , str > {
98
138
use QuoteLevel :: * ;
@@ -404,6 +444,8 @@ pub struct SimpleTypeSerializer<W: Write> {
404
444
pub target : QuoteTarget ,
405
445
/// Defines which XML characters need to be escaped
406
446
pub level : QuoteLevel ,
447
+ /// Format for text content serialization
448
+ pub format : TextFormat ,
407
449
}
408
450
409
451
impl < W : Write > SimpleTypeSerializer < W > {
@@ -427,8 +469,15 @@ impl<W: Write> Serializer for SimpleTypeSerializer<W> {
427
469
write_primitive ! ( ) ;
428
470
429
471
fn serialize_str ( mut self , value : & str ) -> Result < Self :: Ok , Self :: Error > {
430
- if !value. is_empty ( ) {
431
- self . write_str ( & escape_list ( value, self . target , self . level ) ) ?;
472
+ match self . format {
473
+ TextFormat :: CData => {
474
+ write_cdata_content ( & mut self . writer , value) ?;
475
+ }
476
+ TextFormat :: Text => {
477
+ if !value. is_empty ( ) {
478
+ self . write_str ( & escape_list ( value, self . target , self . level ) ) ?;
479
+ }
480
+ }
432
481
}
433
482
Ok ( self . writer )
434
483
}
@@ -1031,6 +1080,7 @@ mod tests {
1031
1080
writer: String :: new( ) ,
1032
1081
target: QuoteTarget :: Text ,
1033
1082
level: QuoteLevel :: Full ,
1083
+ format: TextFormat :: Text ,
1034
1084
} ;
1035
1085
1036
1086
let buffer = $data. serialize( ser) . unwrap( ) ;
@@ -1050,6 +1100,7 @@ mod tests {
1050
1100
writer: & mut buffer,
1051
1101
target: QuoteTarget :: Text ,
1052
1102
level: QuoteLevel :: Full ,
1103
+ format: TextFormat :: Text ,
1053
1104
} ;
1054
1105
1055
1106
match $data. serialize( ser) . unwrap_err( ) {
@@ -1219,4 +1270,39 @@ mod tests {
1219
1270
assert_eq ! ( buffer, "1 2 3" ) ;
1220
1271
}
1221
1272
}
1273
+
1274
+ mod cdata {
1275
+ use super :: * ;
1276
+ use pretty_assertions:: assert_eq;
1277
+
1278
+ macro_rules! serialize_cdata_as {
1279
+ ( $name: ident: $data: expr => $expected: literal) => {
1280
+ #[ test]
1281
+ fn $name( ) {
1282
+ let ser = SimpleTypeSerializer {
1283
+ writer: String :: new( ) ,
1284
+ target: QuoteTarget :: Text ,
1285
+ level: QuoteLevel :: Full ,
1286
+ format: TextFormat :: CData ,
1287
+ } ;
1288
+
1289
+ let buffer = $data. serialize( ser) . unwrap( ) ;
1290
+ assert_eq!( buffer, $expected) ;
1291
+ }
1292
+ } ;
1293
+ }
1294
+
1295
+ serialize_cdata_as ! ( empty_string: "" => "<![CDATA[]]>" ) ;
1296
+ serialize_cdata_as ! ( simple_text: "Hello World" => "<![CDATA[Hello World]]>" ) ;
1297
+ serialize_cdata_as ! ( with_markup: "<tag>content</tag>" => "<![CDATA[<tag>content</tag>]]>" ) ;
1298
+ serialize_cdata_as ! ( with_ampersand: "Tom & Jerry" => "<![CDATA[Tom & Jerry]]>" ) ;
1299
+ serialize_cdata_as ! ( with_quotes: r#"He said "Hello""# => r#"<![CDATA[He said "Hello"]]>"# ) ;
1300
+ serialize_cdata_as ! ( all_xml_chars: "<>&\" '" => "<![CDATA[<>&\" ']]>" ) ;
1301
+
1302
+ serialize_cdata_as ! ( with_cdata_end: "foo]]>bar" => "<![CDATA[foo]]]]><![CDATA[>bar]]>" ) ;
1303
+ serialize_cdata_as ! ( multiple_cdata_ends: "a]]>b]]>c" => "<![CDATA[a]]]]><![CDATA[>b]]]]><![CDATA[>c]]>" ) ;
1304
+ serialize_cdata_as ! ( starts_with_cdata_end: "]]>hello" => "<![CDATA[]]]]><![CDATA[>hello]]>" ) ;
1305
+ serialize_cdata_as ! ( ends_with_cdata_end: "hello]]>" => "<![CDATA[hello]]]]><![CDATA[>]]>" ) ;
1306
+ serialize_cdata_as ! ( only_cdata_end: "]]>" => "<![CDATA[]]]]><![CDATA[>]]>" ) ;
1307
+ }
1222
1308
}
0 commit comments