1extern crate treexml;
40
41#[macro_use]
42extern crate serde_json;
43
44use serde_json::{Map, Number, Value};
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47enum XMLNodeType {
48 Empty,
49 Text,
50 Attributes,
51 TextAndAttributes,
52 Parent,
53 SemiStructured,
54}
55
56fn scan_xml_node(e: &treexml::Element) -> XMLNodeType {
57 if e.children.is_empty() {
58 if e.text.is_none() && e.cdata.is_none() {
59 if e.attributes.is_empty() {
60 XMLNodeType::Empty
61 } else {
62 XMLNodeType::Attributes
63 }
64 } else {
65 if e.attributes.is_empty() {
66 XMLNodeType::Text
67 } else {
68 XMLNodeType::TextAndAttributes
69 }
70 }
71 } else {
72 if e.text.is_some() || e.cdata.is_some() {
73 XMLNodeType::SemiStructured
74 } else {
75 XMLNodeType::Parent
76 }
77 }
78}
79
80fn parse_text(text: &str) -> Value {
81 match text.parse::<f64>() {
82 Ok(v) => match Number::from_f64(v) {
83 Some(v) => {
84 return Value::Number(v);
85 }
86 _ => {}
87 },
88 _ => {}
89 }
90
91 match text.parse::<bool>() {
92 Ok(v) => {
93 return Value::Bool(v);
94 }
95 _ => {}
96 }
97
98 Value::String(text.into())
99}
100
101fn parse_text_contents(e: &treexml::Element) -> Value {
102 let text = format!(
103 "{}{}",
104 &e.text.clone().unwrap_or(String::new()),
105 &e.cdata.clone().unwrap_or(String::new())
106 );
107 parse_text(&text)
108}
109
110fn convert_node_aux(e: &treexml::Element) -> Option<Value> {
111 match scan_xml_node(e) {
112 XMLNodeType::Parent => {
113 let mut data = Map::new();
114 let mut firstpass = std::collections::HashSet::new();
115 let mut vectorized = std::collections::HashSet::new();
116
117 for c in &e.children {
118 match convert_node_aux(c) {
119 Some(v) => {
120 if !firstpass.contains(&c.name) {
121 data.insert(c.name.clone(), v);
122 firstpass.insert(c.name.clone());
123 } else {
124 if !vectorized.contains(&c.name) {
125 let elem = data.remove(&c.name).unwrap();
126 data.insert(c.name.clone(), Value::Array(vec![elem, v]));
127 vectorized.insert(c.name.clone());
128 } else {
129 data.get_mut(&c.name)
130 .unwrap()
131 .as_array_mut()
132 .unwrap()
133 .push(v);
134 }
135 }
136 }
137 _ => {}
138 }
139 }
140 Some(Value::Object(data))
141 }
142 XMLNodeType::Text => Some(parse_text_contents(e)),
143 XMLNodeType::Attributes => Some(Value::Object(
144 e.attributes
145 .clone()
146 .into_iter()
147 .map(|(k, v)| (format!("@{}", k), parse_text(&v)))
148 .collect(),
149 )),
150 XMLNodeType::TextAndAttributes => Some(Value::Object(
151 e.attributes
152 .clone()
153 .into_iter()
154 .map(|(k, v)| (format!("@{}", k), parse_text(&v)))
155 .chain(vec![("#text".to_string(), parse_text_contents(&e))])
156 .collect(),
157 )),
158 _ => None,
159 }
160}
161
162pub fn node2object(e: &treexml::Element) -> Map<String, Value> {
164 let mut data = Map::new();
165 data.insert(e.name.clone(), convert_node_aux(e).unwrap_or(Value::Null));
166 data
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[test]
174 fn node2object_empty() {
175 let fixture = treexml::Element::new("e");
176 let scan_result = XMLNodeType::Empty;
177 let conv_result = json!({ "e": null });
178
179 assert_eq!(scan_result, scan_xml_node(&fixture));
180 assert_eq!(conv_result, Value::Object(node2object(&fixture)));
181 }
182
183 #[test]
184 fn node2object_text() {
185 let mut fixture = treexml::Element::new("player");
186 fixture.text = Some("Kolya".into());
187 let scan_result = XMLNodeType::Text;
188 let conv_result = json!({"player": "Kolya"});
189
190 assert_eq!(scan_result, scan_xml_node(&fixture));
191 assert_eq!(conv_result, Value::Object(node2object(&fixture)));
192 }
193
194 #[test]
195 fn node2object_attributes() {
196 let mut fixture = treexml::Element::new("player");
197 fixture.attributes.insert("score".into(), "9000".into());
198 let scan_result = XMLNodeType::Attributes;
199 let conv_result = json!({ "player": json!({"@score": 9000.0}) });
200
201 assert_eq!(scan_result, scan_xml_node(&fixture));
202 assert_eq!(conv_result, Value::Object(node2object(&fixture)));
203 }
204
205 #[test]
206 fn node2object_text_and_attributes() {
207 let mut fixture = treexml::Element::new("player");
208 fixture.text = Some("Kolya".into());
209 fixture.attributes.insert("score".into(), "9000".into());
210 let scan_result = XMLNodeType::TextAndAttributes;
211 let conv_result = json!({ "player": json!({"#text": "Kolya", "@score": 9000.0}) });
212
213 assert_eq!(scan_result, scan_xml_node(&fixture));
214 assert_eq!(conv_result, Value::Object(node2object(&fixture)));
215 }
216
217 #[test]
218 fn node2object_parent() {
219 let mut fixture = treexml::Element::new("ServerData");
220 fixture.children = vec![
221 {
222 let mut node = treexml::Element::new("Player");
223 node.text = Some("Kolya".into());
224 node
225 },
226 {
227 let mut node = treexml::Element::new("Player");
228 node.text = Some("Petya".into());
229 node
230 },
231 {
232 let mut node = treexml::Element::new("Player");
233 node.text = Some("Misha".into());
234 node
235 },
236 ];
237 let scan_result = XMLNodeType::Parent;
238 let conv_result =
239 json!({ "ServerData": json!({ "Player": [ "Kolya", "Petya", "Misha" ] }) });
240
241 assert_eq!(scan_result, scan_xml_node(&fixture));
242 assert_eq!(conv_result, Value::Object(node2object(&fixture)));
243 }
244}