1use crate::dataset::DataSet;
7use crate::element::Element;
8use crate::value::{DicomDate, DicomDateTime, DicomTime, PersonName, PixelData, Value};
9use dicom_toolkit_core::error::{DcmError, DcmResult};
10use dicom_toolkit_dict::{Tag, Vr};
11
12pub fn to_json(dataset: &DataSet) -> DcmResult<String> {
16 let obj = dataset_to_json_object(dataset)?;
17 serde_json::to_string(&obj).map_err(|e| DcmError::Other(format!("JSON serialize error: {e}")))
18}
19
20pub fn to_json_pretty(dataset: &DataSet) -> DcmResult<String> {
22 let obj = dataset_to_json_object(dataset)?;
23 serde_json::to_string_pretty(&obj)
24 .map_err(|e| DcmError::Other(format!("JSON serialize error: {e}")))
25}
26
27fn dataset_to_json_object(
28 dataset: &DataSet,
29) -> DcmResult<serde_json::Map<String, serde_json::Value>> {
30 let mut map = serde_json::Map::new();
31 for (tag, elem) in dataset.iter() {
32 if tag.is_group_length() || tag.is_delimiter() {
34 continue;
35 }
36 let key = format!("{:04X}{:04X}", tag.group, tag.element);
37 let json_elem = element_to_json(elem)?;
38 map.insert(key, json_elem);
39 }
40 Ok(map)
41}
42
43fn element_to_json(elem: &Element) -> DcmResult<serde_json::Value> {
44 let vr_str = elem.vr.code().to_string();
45
46 let value_json: Option<serde_json::Value> = match &elem.value {
47 Value::Empty => None,
48
49 Value::Strings(v) => {
50 if v.is_empty() {
51 None
52 } else if elem.vr == Vr::PN {
53 let arr: Vec<serde_json::Value> = v
55 .iter()
56 .map(|s| {
57 let mut obj = serde_json::Map::new();
58 if !s.is_empty() {
59 obj.insert("Alphabetic".into(), serde_json::Value::String(s.clone()));
60 }
61 serde_json::Value::Object(obj)
62 })
63 .collect();
64 Some(serde_json::Value::Array(arr))
65 } else {
66 Some(serde_json::Value::Array(
67 v.iter()
68 .map(|s| serde_json::Value::String(s.clone()))
69 .collect(),
70 ))
71 }
72 }
73
74 Value::Uid(s) => Some(serde_json::Value::Array(vec![serde_json::Value::String(
75 s.clone(),
76 )])),
77
78 Value::PersonNames(names) => {
79 let arr: Vec<serde_json::Value> = names
80 .iter()
81 .map(|pn| {
82 let mut obj = serde_json::Map::new();
83 if !pn.alphabetic.is_empty() {
84 obj.insert(
85 "Alphabetic".into(),
86 serde_json::Value::String(pn.alphabetic.clone()),
87 );
88 }
89 if !pn.ideographic.is_empty() {
90 obj.insert(
91 "Ideographic".into(),
92 serde_json::Value::String(pn.ideographic.clone()),
93 );
94 }
95 if !pn.phonetic.is_empty() {
96 obj.insert(
97 "Phonetic".into(),
98 serde_json::Value::String(pn.phonetic.clone()),
99 );
100 }
101 serde_json::Value::Object(obj)
102 })
103 .collect();
104 if arr.is_empty() {
105 None
106 } else {
107 Some(serde_json::Value::Array(arr))
108 }
109 }
110
111 Value::Date(dates) => Some(serde_json::Value::Array(
112 dates
113 .iter()
114 .map(|d| serde_json::Value::String(d.to_string()))
115 .collect(),
116 )),
117 Value::Time(times) => Some(serde_json::Value::Array(
118 times
119 .iter()
120 .map(|t| serde_json::Value::String(t.to_string()))
121 .collect(),
122 )),
123 Value::DateTime(dts) => Some(serde_json::Value::Array(
124 dts.iter()
125 .map(|dt| serde_json::Value::String(dt.to_string()))
126 .collect(),
127 )),
128
129 Value::Ints(v) => Some(serde_json::Value::Array(
130 v.iter().map(|n| serde_json::json!(n)).collect(),
131 )),
132 Value::Decimals(v) => Some(serde_json::Value::Array(
133 v.iter()
134 .map(|n| {
135 if n.is_finite() {
136 serde_json::json!(n)
137 } else {
138 serde_json::Value::Null
139 }
140 })
141 .collect(),
142 )),
143
144 Value::U16(v) => Some(serde_json::Value::Array(
145 v.iter().map(|n| serde_json::json!(n)).collect(),
146 )),
147 Value::I16(v) => Some(serde_json::Value::Array(
148 v.iter().map(|n| serde_json::json!(n)).collect(),
149 )),
150 Value::U32(v) => Some(serde_json::Value::Array(
151 v.iter().map(|n| serde_json::json!(n)).collect(),
152 )),
153 Value::I32(v) => Some(serde_json::Value::Array(
154 v.iter().map(|n| serde_json::json!(n)).collect(),
155 )),
156 Value::U64(v) => Some(serde_json::Value::Array(
157 v.iter().map(|n| serde_json::json!(n)).collect(),
158 )),
159 Value::I64(v) => Some(serde_json::Value::Array(
160 v.iter().map(|n| serde_json::json!(n)).collect(),
161 )),
162 Value::F32(v) => Some(serde_json::Value::Array(
163 v.iter()
164 .map(|n| {
165 if n.is_finite() {
166 serde_json::json!(n)
167 } else {
168 serde_json::Value::Null
169 }
170 })
171 .collect(),
172 )),
173 Value::F64(v) => Some(serde_json::Value::Array(
174 v.iter()
175 .map(|n| {
176 if n.is_finite() {
177 serde_json::json!(n)
178 } else {
179 serde_json::Value::Null
180 }
181 })
182 .collect(),
183 )),
184
185 Value::Tags(tags) => Some(serde_json::Value::Array(
186 tags.iter()
187 .map(|t| serde_json::Value::String(format!("{:04X}{:04X}", t.group, t.element)))
188 .collect(),
189 )),
190
191 Value::Sequence(items) => {
192 let arr: Vec<serde_json::Value> = items
193 .iter()
194 .map(|item| {
195 dataset_to_json_object(item)
196 .map(serde_json::Value::Object)
197 .unwrap_or(serde_json::Value::Null)
198 })
199 .collect();
200 Some(serde_json::Value::Array(arr))
201 }
202
203 Value::U8(bytes) => {
205 use base64::Engine;
206 let b64 = base64::engine::general_purpose::STANDARD.encode(bytes);
207 let mut obj = serde_json::Map::new();
208 obj.insert("vr".into(), serde_json::Value::String(vr_str.clone()));
209 obj.insert("InlineBinary".into(), serde_json::Value::String(b64));
210 return Ok(serde_json::Value::Object(obj));
211 }
212
213 Value::PixelData(pd) => {
214 let bytes = match pd {
215 PixelData::Native { bytes } => bytes.as_slice(),
216 PixelData::Encapsulated { fragments, .. } => {
217 fragments.first().map(|f| f.as_slice()).unwrap_or(&[])
219 }
220 };
221 use base64::Engine;
222 let b64 = base64::engine::general_purpose::STANDARD.encode(bytes);
223 let mut obj = serde_json::Map::new();
224 obj.insert("vr".into(), serde_json::Value::String(vr_str.clone()));
225 obj.insert("InlineBinary".into(), serde_json::Value::String(b64));
226 return Ok(serde_json::Value::Object(obj));
227 }
228 };
229
230 let mut obj = serde_json::Map::new();
231 obj.insert("vr".into(), serde_json::Value::String(vr_str));
232 if let Some(v) = value_json {
233 obj.insert("Value".into(), v);
234 }
235 Ok(serde_json::Value::Object(obj))
236}
237
238pub fn from_json(json: &str) -> DcmResult<DataSet> {
242 let map: serde_json::Map<String, serde_json::Value> = serde_json::from_str(json)
243 .map_err(|e| DcmError::Other(format!("JSON parse error: {e}")))?;
244 json_object_to_dataset(&map)
245}
246
247fn json_object_to_dataset(map: &serde_json::Map<String, serde_json::Value>) -> DcmResult<DataSet> {
248 let mut dataset = DataSet::new();
249 for (key, val) in map {
250 let tag = parse_json_tag(key)?;
251 let elem = json_value_to_element(tag, val)?;
252 dataset.insert(elem);
253 }
254 Ok(dataset)
255}
256
257fn parse_json_tag(key: &str) -> DcmResult<Tag> {
258 if key.len() != 8 {
259 return Err(DcmError::Other(format!("invalid JSON tag key: '{key}'")));
260 }
261 let group = u16::from_str_radix(&key[0..4], 16)
262 .map_err(|_| DcmError::Other(format!("invalid tag group: '{}'", &key[0..4])))?;
263 let element = u16::from_str_radix(&key[4..8], 16)
264 .map_err(|_| DcmError::Other(format!("invalid tag element: '{}'", &key[4..8])))?;
265 Ok(Tag::new(group, element))
266}
267
268fn json_value_to_element(tag: Tag, val: &serde_json::Value) -> DcmResult<Element> {
269 let obj = val
270 .as_object()
271 .ok_or_else(|| DcmError::Other(format!("expected JSON object for tag {tag}")))?;
272
273 let vr_str = obj
274 .get("vr")
275 .and_then(|v| v.as_str())
276 .ok_or_else(|| DcmError::Other(format!("missing 'vr' in JSON element for tag {tag}")))?;
277
278 let vr = Vr::from_bytes([vr_str.as_bytes()[0], vr_str.as_bytes()[1]])
279 .ok_or_else(|| DcmError::Other(format!("unknown VR '{vr_str}' in JSON for tag {tag}")))?;
280
281 if let Some(b64_val) = obj.get("InlineBinary") {
283 use base64::Engine;
284 let b64_str = b64_val
285 .as_str()
286 .ok_or_else(|| DcmError::Other("InlineBinary must be a string".into()))?;
287 let bytes = base64::engine::general_purpose::STANDARD
288 .decode(b64_str)
289 .map_err(|e| DcmError::Other(format!("base64 decode error: {e}")))?;
290 return Ok(Element::bytes(tag, vr, bytes));
291 }
292
293 let values_arr = match obj.get("Value") {
294 None => return Ok(Element::new(tag, vr, Value::Empty)),
295 Some(v) => v
296 .as_array()
297 .ok_or_else(|| DcmError::Other(format!("'Value' must be array for tag {tag}")))?,
298 };
299
300 let value = match vr {
301 Vr::SQ => {
302 let items: DcmResult<Vec<DataSet>> = values_arr
303 .iter()
304 .map(|item| {
305 let item_obj = item
306 .as_object()
307 .ok_or_else(|| DcmError::Other("SQ item must be a JSON object".into()))?;
308 json_object_to_dataset(item_obj)
309 })
310 .collect();
311 Value::Sequence(items?)
312 }
313
314 Vr::PN => {
315 let names: DcmResult<Vec<PersonName>> = values_arr
316 .iter()
317 .map(|pn_val| {
318 if pn_val.is_null() {
319 return Ok(PersonName::parse(""));
320 }
321 let pn_obj = pn_val
322 .as_object()
323 .ok_or_else(|| DcmError::Other("PN value must be a JSON object".into()))?;
324 let alphabetic = pn_obj
325 .get("Alphabetic")
326 .and_then(|v| v.as_str())
327 .unwrap_or("")
328 .to_string();
329 let ideographic = pn_obj
330 .get("Ideographic")
331 .and_then(|v| v.as_str())
332 .unwrap_or("")
333 .to_string();
334 let phonetic = pn_obj
335 .get("Phonetic")
336 .and_then(|v| v.as_str())
337 .unwrap_or("")
338 .to_string();
339 Ok(PersonName {
340 alphabetic,
341 ideographic,
342 phonetic,
343 })
344 })
345 .collect();
346 Value::PersonNames(names?)
347 }
348
349 Vr::DA => {
350 let dates: DcmResult<Vec<DicomDate>> = values_arr
351 .iter()
352 .filter_map(|v| v.as_str())
353 .map(DicomDate::parse)
354 .collect();
355 Value::Date(dates?)
356 }
357
358 Vr::TM => {
359 let times: DcmResult<Vec<DicomTime>> = values_arr
360 .iter()
361 .filter_map(|v| v.as_str())
362 .map(DicomTime::parse)
363 .collect();
364 Value::Time(times?)
365 }
366
367 Vr::DT => {
368 let dts: DcmResult<Vec<DicomDateTime>> = values_arr
369 .iter()
370 .filter_map(|v| v.as_str())
371 .map(DicomDateTime::parse)
372 .collect();
373 Value::DateTime(dts?)
374 }
375
376 Vr::UI => {
377 let uid = values_arr
378 .first()
379 .and_then(|v| v.as_str())
380 .unwrap_or("")
381 .to_string();
382 Value::Uid(uid)
383 }
384
385 Vr::IS => {
386 let ints: Vec<i64> = values_arr.iter().filter_map(|v| v.as_i64()).collect();
387 Value::Ints(ints)
388 }
389
390 Vr::DS => {
391 let decimals: Vec<f64> = values_arr.iter().filter_map(|v| v.as_f64()).collect();
392 Value::Decimals(decimals)
393 }
394
395 Vr::US | Vr::OW => {
396 let vals: Vec<u16> = values_arr
397 .iter()
398 .filter_map(|v| v.as_u64().map(|n| n as u16))
399 .collect();
400 Value::U16(vals)
401 }
402
403 Vr::SS => {
404 let vals: Vec<i16> = values_arr
405 .iter()
406 .filter_map(|v| v.as_i64().map(|n| n as i16))
407 .collect();
408 Value::I16(vals)
409 }
410
411 Vr::UL | Vr::OL => {
412 let vals: Vec<u32> = values_arr
413 .iter()
414 .filter_map(|v| v.as_u64().map(|n| n as u32))
415 .collect();
416 Value::U32(vals)
417 }
418
419 Vr::SL => {
420 let vals: Vec<i32> = values_arr
421 .iter()
422 .filter_map(|v| v.as_i64().map(|n| n as i32))
423 .collect();
424 Value::I32(vals)
425 }
426
427 Vr::UV | Vr::OV => {
428 let vals: Vec<u64> = values_arr.iter().filter_map(|v| v.as_u64()).collect();
429 Value::U64(vals)
430 }
431
432 Vr::SV => {
433 let vals: Vec<i64> = values_arr.iter().filter_map(|v| v.as_i64()).collect();
434 Value::I64(vals)
435 }
436
437 Vr::FL | Vr::OF => {
438 let vals: Vec<f32> = values_arr
439 .iter()
440 .filter_map(|v| v.as_f64().map(|n| n as f32))
441 .collect();
442 Value::F32(vals)
443 }
444
445 Vr::FD | Vr::OD => {
446 let vals: Vec<f64> = values_arr.iter().filter_map(|v| v.as_f64()).collect();
447 Value::F64(vals)
448 }
449
450 Vr::AT => {
451 let tags: DcmResult<Vec<Tag>> = values_arr
452 .iter()
453 .filter_map(|v| v.as_str())
454 .map(|s| {
455 if s.len() != 8 {
456 return Err(DcmError::Other(format!("invalid AT value: '{s}'")));
457 }
458 let g = u16::from_str_radix(&s[0..4], 16)
459 .map_err(|_| DcmError::Other(format!("bad AT group: {s}")))?;
460 let e = u16::from_str_radix(&s[4..8], 16)
461 .map_err(|_| DcmError::Other(format!("bad AT element: {s}")))?;
462 Ok(Tag::new(g, e))
463 })
464 .collect();
465 Value::Tags(tags?)
466 }
467
468 _ => {
470 let strings: Vec<String> = values_arr
471 .iter()
472 .filter_map(|v| v.as_str().map(str::to_string))
473 .collect();
474 Value::Strings(strings)
475 }
476 };
477
478 Ok(Element::new(tag, vr, value))
479}
480
481#[cfg(test)]
482mod tests {
483 use super::*;
484 use dicom_toolkit_dict::tags;
485
486 fn make_dataset() -> DataSet {
487 let mut ds = DataSet::new();
488 ds.set_string(tags::PATIENT_NAME, Vr::PN, "Doe^John");
489 ds.set_string(tags::PATIENT_ID, Vr::LO, "PAT-001");
490 ds.set_uid(tags::SOP_INSTANCE_UID, "1.2.3.4.5");
491 ds.set_u16(tags::SAMPLES_PER_PIXEL, 1);
492 ds.set_u16(tags::ROWS, 512);
493 ds.set_u16(tags::COLUMNS, 512);
494 ds
495 }
496
497 #[test]
498 fn serialize_basic_dataset() {
499 let ds = make_dataset();
500 let json = to_json(&ds).unwrap();
501 assert!(json.contains("00100010"), "should contain PatientName tag");
503 assert!(json.contains("00100020"), "should contain PatientID tag");
504 assert!(
505 json.contains("0020000D") || json.contains("00080018"),
506 "should contain UID tag"
507 );
508 }
509
510 #[test]
511 fn roundtrip_string_element() {
512 let mut ds = DataSet::new();
513 ds.set_string(tags::PATIENT_ID, Vr::LO, "PAT-123");
514
515 let json = to_json(&ds).unwrap();
516 let parsed = from_json(&json).unwrap();
517
518 assert_eq!(parsed.get_string(tags::PATIENT_ID), Some("PAT-123"));
519 }
520
521 #[test]
522 fn roundtrip_uid_element() {
523 let mut ds = DataSet::new();
524 ds.set_uid(tags::SOP_INSTANCE_UID, "1.2.840.10008.5.1.4.1.1.2");
525
526 let json = to_json(&ds).unwrap();
527 let parsed = from_json(&json).unwrap();
528
529 assert_eq!(
530 parsed.get_string(tags::SOP_INSTANCE_UID),
531 Some("1.2.840.10008.5.1.4.1.1.2")
532 );
533 }
534
535 #[test]
536 fn roundtrip_u16_element() {
537 let mut ds = DataSet::new();
538 ds.set_u16(tags::ROWS, 256);
539
540 let json = to_json(&ds).unwrap();
541 let parsed = from_json(&json).unwrap();
542
543 assert_eq!(parsed.get_u16(tags::ROWS), Some(256));
544 }
545
546 #[test]
547 fn roundtrip_sequence() {
548 let mut ds = DataSet::new();
549 let mut item = DataSet::new();
550 item.set_string(tags::PATIENT_ID, Vr::LO, "ITEM-1");
551 ds.set_sequence(tags::REFERENCED_SOP_SEQUENCE, vec![item]);
552
553 let json = to_json(&ds).unwrap();
554 let parsed = from_json(&json).unwrap();
555
556 let items = parsed.get_items(tags::REFERENCED_SOP_SEQUENCE).unwrap();
557 assert_eq!(items.len(), 1);
558 assert_eq!(items[0].get_string(tags::PATIENT_ID), Some("ITEM-1"));
559 }
560
561 #[test]
562 fn roundtrip_person_name() {
563 let mut ds = DataSet::new();
564 ds.set_string(tags::PATIENT_NAME, Vr::PN, "Smith^John^^Dr.");
565
566 let json = to_json(&ds).unwrap();
567 assert!(json.contains("Alphabetic"), "PN should use Alphabetic key");
568
569 let parsed = from_json(&json).unwrap();
570 assert!(parsed.contains(tags::PATIENT_NAME));
572 }
573
574 #[test]
575 fn invalid_json_returns_error() {
576 assert!(from_json("not json").is_err());
577 assert!(from_json("[]").is_err(), "array at root should fail");
578 }
579
580 #[test]
581 fn invalid_tag_key_returns_error() {
582 assert!(from_json(r#"{"00100": {"vr": "LO"}}"#).is_err());
584 assert!(from_json(r#"{"GGGGEEEE": {"vr": "LO"}}"#).is_err());
586 }
587
588 #[test]
589 fn pretty_print_is_valid_json() {
590 let ds = make_dataset();
591 let pretty = to_json_pretty(&ds).unwrap();
592 let reparsed: serde_json::Value = serde_json::from_str(&pretty).unwrap();
594 assert!(reparsed.is_object());
595 }
596}