dicom_json/ser/
mod.rs

1//! DICOM JSON serialization module
2
3use std::io::Write;
4
5use crate::DicomJson;
6use dicom_core::{
7    header::Header, value::PixelFragmentSequence, DicomValue, PrimitiveValue, Tag, VR,
8};
9use dicom_dictionary_std::StandardDataDictionary;
10use dicom_object::{mem::InMemElement, DefaultDicomObject, InMemDicomObject};
11use serde::{ser::SerializeMap, Serialize, Serializer};
12
13use self::value::{AsNumbers, AsPersonNames, AsStrings, InlineBinary};
14mod value;
15
16/// Serialize a piece of DICOM data as a string of JSON.
17pub fn to_string<T>(data: T) -> Result<String, serde_json::Error>
18where
19    DicomJson<T>: From<T> + Serialize,
20{
21    serde_json::to_string(&DicomJson::from(data))
22}
23
24/// Serialize a piece of DICOM data as a pretty-printed string of JSON.
25pub fn to_string_pretty<T>(data: T) -> Result<String, serde_json::Error>
26where
27    DicomJson<T>: From<T> + Serialize,
28{
29    serde_json::to_string_pretty(&DicomJson::from(data))
30}
31
32/// Serialize a piece of DICOM data as a serde JSON value.
33pub fn to_value<T>(data: T) -> Result<serde_json::Value, serde_json::Error>
34where
35    DicomJson<T>: From<T> + Serialize,
36{
37    serde_json::to_value(DicomJson::from(data))
38}
39
40/// Serialize a piece of DICOM data to a vector of bytes.
41pub fn to_vec<T>(data: T) -> Result<Vec<u8>, serde_json::Error>
42where
43    DicomJson<T>: From<T> + Serialize,
44{
45    serde_json::to_vec(&DicomJson::from(data))
46}
47
48/// Serialize a piece of DICOM data to a byte writer.
49pub fn to_writer<W, T>(writer: W, data: T) -> Result<(), serde_json::Error>
50where
51    DicomJson<T>: From<T> + Serialize,
52    W: Write,
53{
54    serde_json::to_writer(writer, &DicomJson::from(data))
55}
56
57impl<'a, D> From<&'a DefaultDicomObject<D>> for DicomJson<&'a DefaultDicomObject<D>> {
58    fn from(value: &'a DefaultDicomObject<D>) -> Self {
59        Self(value)
60    }
61}
62
63impl<'a, D> Serialize for DicomJson<&'a DefaultDicomObject<D>>
64where
65    D: 'a,
66{
67    /// Serializes the DICOM file as a JSON map
68    /// containing one entry per data element (indexed by tag),
69    /// _plus_ the data elements described by its file meta table.
70    ///
71    /// To exclude the file meta group data instead,
72    /// dereference the value into the underlying DICOM object first
73    /// (e.g. via `&*obj`).
74    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
75    where
76        S: Serializer,
77    {
78        let mut ser = serializer.serialize_map(None)?;
79
80        for e in self.0.meta().to_element_iter() {
81            let tag = e.tag();
82            let DicomValue::Primitive(value) = e.value() else {
83                continue;
84            };
85            let e = InMemElement::<StandardDataDictionary>::new(e.tag(), e.vr(), value.clone());
86            ser.serialize_entry(&DicomJson(tag), &DicomJson(&e))?;
87        }
88
89        let inner: &InMemDicomObject<_> = &**self.0;
90        for e in inner {
91            let tag = e.tag();
92            ser.serialize_entry(&DicomJson(tag), &DicomJson(e))?;
93        }
94
95        ser.end()
96    }
97}
98
99impl<D> From<DefaultDicomObject<D>> for DicomJson<DefaultDicomObject<D>> {
100    fn from(value: DefaultDicomObject<D>) -> Self {
101        Self(value)
102    }
103}
104
105impl<D> Serialize for DicomJson<DefaultDicomObject<D>> {
106    /// Serializes the DICOM file as a JSON map
107    /// containing one entry per data element (indexed by tag),
108    /// _plus_ the data elements described by its file meta table.
109    ///
110    /// To exclude the file meta group data instead,
111    /// dereference the value into the underlying DICOM object first
112    /// (e.g. via `&*obj`).
113    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
114    where
115        S: Serializer,
116    {
117        DicomJson(&self.0).serialize(serializer)
118    }
119}
120
121impl<'a, D> From<&'a InMemDicomObject<D>> for DicomJson<&'a InMemDicomObject<D>> {
122    fn from(value: &'a InMemDicomObject<D>) -> Self {
123        Self(value)
124    }
125}
126
127impl<'a, D> Serialize for DicomJson<&'a InMemDicomObject<D>>
128where
129    D: 'a,
130{
131    /// Serializes the DICOM object as a JSON map
132    /// containing one entry per data element,
133    /// indexed by tag.
134    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
135    where
136        S: Serializer,
137    {
138        serializer.collect_map(self.0.into_iter().map(|e| {
139            let tag = e.tag();
140            (DicomJson(tag), DicomJson(e))
141        }))
142    }
143}
144
145impl<D> From<InMemDicomObject<D>> for DicomJson<InMemDicomObject<D>> {
146    fn from(value: InMemDicomObject<D>) -> Self {
147        Self(value)
148    }
149}
150
151impl<D> Serialize for DicomJson<InMemDicomObject<D>> {
152    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
153    where
154        S: Serializer,
155    {
156        DicomJson(&self.0).serialize(serializer)
157    }
158}
159
160impl<'a, D> From<&'a [InMemDicomObject<D>]> for DicomJson<&'a [InMemDicomObject<D>]> {
161    fn from(value: &'a [InMemDicomObject<D>]) -> Self {
162        Self(value)
163    }
164}
165
166impl<D> Serialize for DicomJson<&'_ [InMemDicomObject<D>]> {
167    /// Serializes the sequence of DICOM objects into a JSON array.
168    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
169    where
170        S: Serializer,
171    {
172        serializer.collect_seq(self.0.iter().map(DicomJson::from))
173    }
174}
175
176impl<D> From<Vec<InMemDicomObject<D>>> for DicomJson<Vec<InMemDicomObject<D>>> {
177    fn from(value: Vec<InMemDicomObject<D>>) -> Self {
178        Self(value)
179    }
180}
181
182impl<D> Serialize for DicomJson<Vec<InMemDicomObject<D>>> {
183    /// Serializes the sequence of DICOM objects into a JSON array.
184    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
185    where
186        S: Serializer,
187    {
188        DicomJson(self.0.as_slice()).serialize(serializer)
189    }
190}
191
192impl<'a, D> From<&'a InMemElement<D>> for DicomJson<&'a InMemElement<D>> {
193    fn from(value: &'a InMemElement<D>) -> Self {
194        Self(value)
195    }
196}
197
198impl<D> Serialize for DicomJson<&'_ InMemElement<D>> {
199    /// Serializes the data element as a single JSON map.
200    ///
201    /// The fields present will be:
202    /// - `"vr"`, containing the value representation;
203    /// - Either `"Value"` (as an array of values)
204    ///   or `"InlineBinary"` (binary data in base64),
205    ///   if the value is not empty.
206    ///
207    /// The DICOM tag is not encoded,
208    /// as it is typically serialized as the entry key within a data set.
209    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
210    where
211        S: Serializer,
212    {
213        let mut serializer = serializer.serialize_map(None)?;
214        let vr = self.0.vr();
215        serializer.serialize_entry("vr", vr.to_string())?;
216
217        match self.0.value() {
218            DicomValue::Sequence(seq) => {
219                serializer.serialize_entry("Value", &DicomJson(seq.items()))?;
220            }
221            DicomValue::PixelSequence(_seq) => {
222                //serializer.serialize_entry("Value", &DicomJson(seq))?;
223            }
224            DicomValue::Primitive(PrimitiveValue::Empty) => {
225                // no-op
226            }
227            DicomValue::Primitive(v) => match vr {
228                VR::AE
229                | VR::AS
230                | VR::AT
231                | VR::CS
232                | VR::DA
233                | VR::DT
234                | VR::LO
235                | VR::LT
236                | VR::SH
237                | VR::UC
238                | VR::UI
239                | VR::UR
240                | VR::TM
241                | VR::ST
242                | VR::UT => {
243                    serializer.serialize_entry("Value", &AsStrings::from(v))?;
244                }
245                VR::PN => {
246                    serializer.serialize_entry("Value", &AsPersonNames::from(v))?;
247                }
248                VR::FD
249                | VR::IS
250                | VR::FL
251                | VR::DS
252                | VR::SL
253                | VR::SS
254                | VR::SV
255                | VR::UL
256                | VR::US
257                | VR::UV => {
258                    serializer.serialize_entry("Value", &AsNumbers::from(v))?;
259                }
260                VR::OB | VR::OD | VR::OF | VR::OL | VR::OV | VR::OW | VR::UN => {
261                    serializer.serialize_entry("InlineBinary", &InlineBinary::from(v))?;
262                }
263                VR::SQ => unreachable!("unexpected VR SQ in primitive value"),
264            },
265        }
266
267        serializer.end()
268    }
269}
270
271impl<D> From<InMemElement<D>> for DicomJson<InMemElement<D>> {
272    fn from(value: InMemElement<D>) -> Self {
273        Self(value)
274    }
275}
276
277impl<D> Serialize for DicomJson<InMemElement<D>> {
278    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
279    where
280        S: Serializer,
281    {
282        DicomJson(&self.0).serialize(serializer)
283    }
284}
285
286impl Serialize for DicomJson<&PixelFragmentSequence<Vec<u8>>> {
287    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
288    where
289        S: Serializer,
290    {
291        let offset_table = self.inner().offset_table();
292        let fragments = self.inner().fragments();
293        let mut map = serializer.serialize_map(Some(2))?;
294        map.serialize_entry("Offset Table", offset_table)?;
295        map.serialize_entry("Pixel Fragments", fragments)?;
296        map.end()
297    }
298}
299
300impl From<Tag> for DicomJson<Tag> {
301    fn from(value: Tag) -> Self {
302        Self(value)
303    }
304}
305
306impl Serialize for DicomJson<Tag> {
307    /// Serializes the DICOM tag as a single string in uppercase hexadecimal,
308    /// with no separators or delimiters (`"GGGGEEEE"`).
309    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
310    where
311        S: Serializer,
312    {
313        let Tag(g, e) = self.0;
314        serializer.serialize_str(&format!("{g:04X}{e:04X}"))
315    }
316}
317
318#[cfg(test)]
319mod tests {
320    use pretty_assertions::assert_eq;
321
322    use dicom_core::value::DataSetSequence;
323    use dicom_core::Length;
324    use dicom_core::{dicom_value, value::DicomDate};
325    use dicom_dictionary_std::tags;
326    use serde_json::json;
327
328    use super::*;
329
330    #[test]
331    fn serialize_simple_data_elements() {
332        let all_data = vec![
333            InMemElement::new(
334                Tag(0x0008, 0x0005),
335                VR::CS,
336                PrimitiveValue::from("ISO_IR 192"),
337            ),
338            InMemElement::new(
339                Tag(0x0008, 0x0020),
340                VR::DA,
341                PrimitiveValue::from(DicomDate::from_ymd(2013, 4, 9).unwrap()),
342            ),
343            InMemElement::new(
344                Tag(0x0008, 0x0061),
345                VR::CS,
346                dicom_value!(Strs, ["CT", "PET"]),
347            ),
348            InMemElement::new(
349                Tag(0x0008, 0x0090),
350                VR::PN,
351                PrimitiveValue::from("^Bob^^Dr."),
352            ),
353            InMemElement::new(
354                Tag(0x0009, 0x1002),
355                VR::UN,
356                dicom_value!(U8, [0xcf, 0x4c, 0x7d, 0x73, 0xcb, 0xfb]),
357            ),
358            InMemElement::new(tags::PATIENT_AGE, VR::AS, PrimitiveValue::from("30Y")),
359        ];
360
361        let obj = InMemDicomObject::from_element_iter(all_data);
362
363        assert_eq!(
364            to_value(&obj).unwrap(),
365            json!({
366                "00080005": {
367                    "vr": "CS",
368                    "Value": [ "ISO_IR 192" ]
369                },
370                "00080020": {
371                    "vr": "DA",
372                    "Value": [ "20130409" ]
373                },
374                "00080061": {
375                    "vr": "CS",
376                    "Value": [
377                        "CT",
378                        "PET"
379                    ]
380                },
381                "00080090": {
382                    "vr": "PN",
383                    "Value": [
384                      {
385                        "Alphabetic": "^Bob^^Dr."
386                      }
387                    ]
388                },
389                "00091002": {
390                    "vr": "UN",
391                    "InlineBinary": "z0x9c8v7"
392                },
393                "00101010": {
394                    "vr": "AS",
395                    "Value": [ "30Y" ]
396                }
397            }),
398        );
399    }
400
401    #[test]
402    fn serialize_sequence_elements() {
403        let obj = InMemDicomObject::from_element_iter([InMemElement::new(
404            tags::SHARED_FUNCTIONAL_GROUPS_SEQUENCE,
405            VR::SQ,
406            DataSetSequence::new(
407                vec![
408                    // Item 0
409                    InMemDicomObject::from_element_iter([InMemElement::new(
410                        tags::CT_ACQUISITION_TYPE_SEQUENCE,
411                        VR::SQ,
412                        DataSetSequence::new(
413                            vec![
414                                // Item 0
415                                InMemDicomObject::from_element_iter([
416                                    InMemElement::new(
417                                        tags::ACQUISITION_TYPE,
418                                        VR::CS,
419                                        PrimitiveValue::from("SEQUENCED"),
420                                    ),
421                                    InMemElement::new(
422                                        tags::CONSTANT_VOLUME_FLAG,
423                                        VR::CS,
424                                        PrimitiveValue::from("NO"),
425                                    ),
426                                    InMemElement::new(
427                                        tags::FLUOROSCOPY_FLAG,
428                                        VR::CS,
429                                        PrimitiveValue::from("NO"),
430                                    ),
431                                ]),
432                            ],
433                            Length::UNDEFINED,
434                        ),
435                    )]),
436                    // Item 1
437                    InMemDicomObject::from_element_iter([InMemElement::new(
438                        tags::CT_ACQUISITION_DETAILS_SEQUENCE,
439                        VR::SQ,
440                        DataSetSequence::new(
441                            vec![InMemDicomObject::from_element_iter([
442                                InMemElement::new(
443                                    tags::DATA_COLLECTION_DIAMETER,
444                                    VR::DS,
445                                    PrimitiveValue::from("500.08"),
446                                ),
447                                InMemElement::new(
448                                    tags::GANTRY_DETECTOR_TILT,
449                                    VR::DS,
450                                    PrimitiveValue::from("0.00"),
451                                ),
452                                InMemElement::new(
453                                    tags::TABLE_HEIGHT,
454                                    VR::DS,
455                                    PrimitiveValue::from("160.000"),
456                                ),
457                                InMemElement::new(
458                                    tags::ROTATION_DIRECTION,
459                                    VR::CS,
460                                    PrimitiveValue::from("CW"),
461                                ),
462                            ])],
463                            Length::UNDEFINED,
464                        ),
465                    )]),
466                ],
467                Length::UNDEFINED,
468            ),
469        )]);
470
471        assert_eq!(
472            to_value(obj).unwrap(),
473            json!({
474                // shared functional groups
475                "52009229": {
476                    "vr": "SQ",
477                    "Value": [
478                        // CT acquisition type
479                        {
480                            "00189301": {
481                                "vr": "SQ",
482                                "Value": [
483                                    {
484                                        "00189302": {
485                                            "vr": "CS",
486                                            "Value": ["SEQUENCED"]
487                                        },
488                                        "00189333": {
489                                            "vr": "CS",
490                                            "Value": ["NO"]
491                                        },
492                                        "00189334": {
493                                            "vr": "CS",
494                                            "Value": ["NO"]
495                                        }
496                                    }
497                                ]
498                            }
499                        },
500                        // CT acquisition details
501                        {
502                            "00189304": {
503                                "vr": "SQ",
504                                "Value": [
505                                    {
506                                        "00180090": {
507                                            "vr": "DS",
508                                            "Value": ["500.08"]
509                                        },
510                                        "00181120": {
511                                            "vr": "DS",
512                                            "Value": ["0.00"]
513                                        },
514                                        "00181130": {
515                                            "vr": "DS",
516                                            "Value": ["160.000"]
517                                        },
518                                        "00181140": {
519                                            "vr": "CS",
520                                            "Value": ["CW"]
521                                        },
522                                    }
523                                ]
524                            }
525                        }
526                    ]
527                }
528            }),
529        );
530    }
531
532    #[test]
533    fn write_full_file_to_json() {
534        let sc_rgb_rle = dicom_test_files::path("pydicom/SC_rgb_rle.dcm").unwrap();
535
536        let obj = dicom_object::OpenFileOptions::new()
537            .read_until(Tag(0x0010, 0))
538            .open_file(sc_rgb_rle)
539            .expect("Failed to open test file");
540
541        let value = serde_json::to_value(DicomJson::from(obj)).unwrap();
542
543        assert_eq!(
544            value,
545            json!({
546                "00020000": {
547                    "vr": "UL",
548                    "Value": [238]
549                },
550                "00020001": {
551                    "vr": "OB",
552                    "InlineBinary": "AAE="
553                },
554                "00020002": {
555                    "vr": "UI",
556                    "Value": ["1.2.840.10008.5.1.4.1.1.7"]
557                },
558                "00020003": {
559                    "vr": "UI",
560                    "Value": ["1.2.826.0.1.3680043.8.498.49043964482360854182530167603505525116"]
561                },
562                "00020010": {
563                    "vr": "UI",
564                    "Value": ["1.2.840.10008.1.2.5"]
565                },
566                "00020012": {
567                    "vr": "UI",
568                    "Value": ["1.2.826.0.1.3680043.2.1143.107.104.103.115.2.8.4"]
569                },
570                "00020013": {
571                    "vr": "SH",
572                    "Value": ["GDCM 2.8.4"]
573                },
574                "00020016": {
575                    "vr": "AE",
576                    "Value": ["gdcmconv"]
577                },
578                "00080005": {
579                    "vr": "CS",
580                    "Value": ["ISO_IR 192"]
581                },
582                "00080008": {
583                    "vr": "CS",
584                    "Value": ["DERIVED", "SECONDARY", "OTHER"]
585                },
586                "00080016": {
587                    "vr": "UI",
588                    "Value": ["1.2.840.10008.5.1.4.1.1.7"]
589                },
590                "00080018": {
591                    "vr": "UI",
592                    "Value": ["1.2.826.0.1.3680043.8.498.49043964482360854182530167603505525116"]
593                },
594                "00080020": {
595                    "vr": "DA",
596                    "Value": ["20170101"]
597                },
598                "00080023": { "vr": "DA" },
599                "0008002A": { "vr": "DT" },
600                "00080030": {
601                    "vr": "TM",
602                    "Value": ["120000"],
603                },
604                "00080033": { "vr": "TM" },
605                "00080050": { "vr": "SH" },
606                "00080060": {
607                    "vr": "CS",
608                    "Value": ["OT"]
609                },
610                "00080064": {
611                    "vr": "CS",
612                    "Value": ["SYN"]
613                },
614                "00080090": {
615                    "vr": "PN",
616                    "Value": [
617                        {
618                            "Alphabetic": "Moriarty^James"
619                        }
620                    ]
621                }
622            })
623        );
624    }
625}