quick_xml_to_json/
lib.rs

1#![warn(clippy::pedantic)]
2
3mod decoders;
4mod errors;
5mod frames;
6
7use crate::frames::AttributesWriter;
8use decoders::decode_text;
9pub use errors::XmlToJsonError;
10use quick_xml::Reader;
11use quick_xml::events::Event;
12use std::io::{BufReader, Read, Write};
13
14static CHILDREN_KEY: &str = "#c";
15static TEXT_NODE_KEY: &str = "#t";
16static MB: usize = 1024 * 1024;
17
18/// Convert XML to JSON
19///
20/// # Example Usage
21///
22/// ```
23/// use quick_xml_to_json::xml_to_json;
24///
25/// let xml = r#"<root><parent id="1"><child>Value</child></parent></root>"#;
26/// let expected_json = serde_json::json!({
27///     "root": {
28///         "#c": [
29///             {
30///                 "parent": {
31///                     "@id": "1",
32///                     "#c": [
33///                         { "child": { "#t": "Value" } }
34///                     ]
35///                 }
36///             }
37///         ]
38///     }
39/// });
40///
41///
42/// let mut output = Vec::new();
43/// assert!(xml_to_json(xml.as_bytes(), &mut output).is_ok());
44///
45/// assert_eq!(
46///   expected_json,
47///   serde_json::from_slice::<serde_json::Value>(&output).unwrap()
48/// );
49/// ```
50///
51/// # Errors
52///
53/// This may error when:
54///
55/// * reading XML
56/// * serializing strings to JSON
57/// * converting a String to a byte array
58/// * writing to the buffer
59pub fn xml_to_json<R: Read, W: Write>(reader: R, out: W) -> Result<(), XmlToJsonError> {
60    let mut writer = std::io::BufWriter::with_capacity(MB * 2, out);
61    let mut xml = Reader::from_reader(BufReader::new(reader));
62    let mut buf = Vec::new();
63    let mut stack: Vec<frames::Element> = Vec::new();
64
65    loop {
66        match (xml.read_event_into(&mut buf)?, stack.last_mut()) {
67            // # Process root element
68            //
69            // Open root element that has children
70            (Event::Start(e), None) => {
71                let mut frame = frames::Element::from_element(&e, &xml)?;
72
73                frame.open(&mut writer)?;
74                frame.process_element_attributes(&e, &xml, &mut writer)?;
75
76                stack.push(frame);
77            }
78
79            // Open root that has no children
80            (Event::Empty(e), None) => {
81                let mut frame = frames::EmptyNode::from_element(&e, &xml)?;
82
83                frame.open(&mut writer)?;
84                frame.process_element_attributes(&e, &xml, &mut writer)?;
85                frame.close(&mut writer)?;
86
87                writer.flush()?;
88                return Ok(());
89            }
90
91            // # Process child element
92            //
93            // Open child element that has children
94            (Event::Start(e), Some(parent)) => {
95                parent.begin_child(&mut writer)?;
96
97                let mut frame = frames::Element::from_element(&e, &xml)?;
98
99                frame.open(&mut writer)?;
100                frame.process_element_attributes(&e, &xml, &mut writer)?;
101
102                stack.push(frame);
103            }
104
105            // Open child element that has no children
106            (Event::Empty(e), Some(parent)) => {
107                parent.begin_child(&mut writer)?;
108
109                let mut frame = frames::EmptyNode::from_element(&e, &xml)?;
110
111                frame.open(&mut writer)?;
112                frame.process_element_attributes(&e, &xml, &mut writer)?;
113                frame.close(&mut writer)?;
114            }
115
116            // Process a text node of an element
117            (Event::Text(t), Some(frame)) => {
118                let text = decode_text(&xml, &t)?;
119                frame.push_text(&text);
120            }
121
122            // Close out the current node on the stack
123            (Event::End(_), _) => {
124                if let Some(mut frame) = stack.pop() {
125                    frame.close(&mut writer)?;
126
127                    // If there's nothing else on the stack, we're done
128                    if stack.is_empty() {
129                        writer.flush()?;
130                        return Ok(());
131                    }
132                }
133            }
134
135            (Event::Eof, _) => break,
136            _ => {}
137        }
138
139        buf.clear();
140    }
141
142    Err(XmlToJsonError::InvalidXML)
143}
144
145#[cfg(test)]
146mod tests {
147
148    #[test]
149    fn test_nested_structure() {
150        let xml = r#"
151            <root>
152                <child1 attr1="value1">
153                    <subchild>Text 1</subchild>
154                    <subchild>Text 2</subchild>
155                    <subchild>Text 3</subchild>
156                </child1>
157                <child2 attr2="value2" />
158                <child1 attr2="value2" attr3="value3" attr1="value1">
159                    <subchild>Text 2</subchild>
160                    <subchild>Text 1</subchild>
161                    <subchild>Text 3</subchild>
162                </child1>
163            </root>
164        "#;
165
166        let expected_json = serde_json::json!({
167            "root": {
168                "#c": [
169                    {
170                        "child1": {
171                            "@attr1": "value1",
172                            "#c": [
173                                {
174                                    "subchild": {
175                                        "#t": "Text 1"
176                                    }
177                                },
178                                {
179                                    "subchild": {
180                                        "#t": "Text 2"
181                                    }
182                                },
183                                {
184                                    "subchild": {
185                                        "#t": "Text 3"
186                                    }
187                                },
188                            ]
189                        }
190                    },
191                    {
192                        "child2": {
193                            "@attr2": "value2"
194                        }
195                    },
196                    {
197                        "child1": {
198                            "@attr3": "value3",
199                            "@attr2": "value2",
200                            "@attr1": "value1",
201                            "#c": [
202                                {
203                                    "subchild": {
204                                        "#t": "Text 2"
205                                    }
206                                },
207                                {
208                                    "subchild": {
209                                        "#t": "Text 1"
210                                    }
211                                },
212                                {
213                                    "subchild": {
214                                        "#t": "Text 3"
215                                    }
216                                },
217                            ]
218                        }
219                    },
220                ]
221            }
222        });
223
224        assert_eq!(expected_json, convert_xml_to_json(xml));
225    }
226
227    #[test]
228    fn test_basic_xml_to_json() {
229        let xml = r#"<users count="3">
230  <user age="40">Jane Doe</user>
231  <user age="42">John Doe</user>
232  <user age="12">Jim Doe</user>
233</users>"#;
234
235        let expected_json = serde_json::json!({
236            "users": {
237                "@count": "3",
238                "#c": [
239                    {
240                        "user": {
241                            "@age": "40",
242                            "#t": "Jane Doe"
243                        }
244                    },
245                    {
246                        "user": {
247                            "@age": "42",
248                            "#t": "John Doe"
249                        }
250                    },
251                    {
252                        "user": {
253                            "@age": "12",
254                            "#t": "Jim Doe"
255                        }
256                    }
257                ]
258            }
259        });
260
261        assert_eq!(expected_json, convert_xml_to_json(xml));
262    }
263
264    #[test]
265    fn test_single_element_with_text() {
266        let xml = "<name>John Doe</name>";
267        let expected_json = serde_json::json!({
268            "name": {
269                "#t": "John Doe"
270            }
271        });
272
273        assert_eq!(expected_json, convert_xml_to_json(xml));
274    }
275
276    #[test]
277    fn test_element_with_attributes_only() {
278        let xml = r#"<div class="container" id="main"></div>"#;
279        let expected_json = serde_json::json!({
280            "div": {
281                "@class": "container",
282                "@id": "main"
283            }
284        });
285
286        assert_eq!(expected_json, convert_xml_to_json(xml));
287    }
288
289    #[test]
290    fn test_nested_elements() {
291        let xml = r#"<root><parent id="1"><child>Value</child></parent></root>"#;
292        let expected_json = serde_json::json!({
293            "root": {
294                "#c": [
295                    {
296                        "parent": {
297                            "@id": "1",
298                            "#c": [
299                                { "child": { "#t": "Value" } }
300                            ]
301                        }
302                    }
303                ]
304            }
305        });
306
307        assert_eq!(expected_json, convert_xml_to_json(xml));
308    }
309
310    #[test]
311    fn test_empty_xml() {
312        assert!(super::xml_to_json("".as_bytes(), Vec::new()).is_err());
313    }
314
315    #[test]
316    fn test_malformed_xml() {
317        assert!(super::xml_to_json("<root><unclosed>".as_bytes(), Vec::new()).is_err());
318    }
319
320    #[test]
321    fn test_empty_nodes() {
322        let xml = "<main><br /></main>";
323        let expected_json = serde_json::json!({
324            "main": {
325                "#c": [
326                    { "br": {}}
327                ]
328            }
329        });
330
331        assert_eq!(expected_json, convert_xml_to_json(xml));
332    }
333
334    #[test]
335    fn test_empty_node_at_root() {
336        let xml = "<br />";
337        let expected_json = serde_json::json!({ "br": {} });
338
339        assert_eq!(expected_json, convert_xml_to_json(xml));
340    }
341
342    #[test]
343    fn test_empty_node_at_root_with_attributes() {
344        let xml = r#"<br id="5" />"#;
345        let expected_json = serde_json::json!({ "br": { "@id": "5" } });
346
347        assert_eq!(expected_json, convert_xml_to_json(xml));
348    }
349
350    fn convert_xml_to_json(xml: &str) -> serde_json::Value {
351        let mut output = Vec::new();
352        super::xml_to_json(xml.as_bytes(), &mut output).unwrap();
353
354        serde_json::from_slice(&output).unwrap()
355    }
356}