dicom_json/de/
mod.rs

1//! DICOM JSON deserialization module
2
3use std::{marker::PhantomData, str::FromStr};
4
5use crate::DicomJson;
6use dicom_core::{
7    value::{InMemFragment, Value, C},
8    DataDictionary, DataElement, PrimitiveValue, Tag, VR,
9};
10use dicom_object::InMemDicomObject;
11use serde::de::{Deserialize, DeserializeOwned, Error as _, Visitor};
12
13use self::value::{BulkDataUri, DicomJsonPerson, NumberOrText};
14
15mod value;
16
17/// Deserialize a piece of DICOM data from a string of JSON.
18pub fn from_str<'a, T>(string: &'a str) -> Result<T, serde_json::Error>
19where
20    DicomJson<T>: Deserialize<'a>,
21{
22    serde_json::from_str::<DicomJson<T>>(string).map(DicomJson::into_inner)
23}
24
25/// Deserialize a piece of DICOM data from a byte slice.
26pub fn from_slice<'a, T>(slice: &'a [u8]) -> Result<T, serde_json::Error>
27where
28    DicomJson<T>: Deserialize<'a>,
29{
30    serde_json::from_slice::<DicomJson<T>>(slice).map(DicomJson::into_inner)
31}
32
33/// Deserialize a piece of DICOM data from a standard byte reader.
34pub fn from_reader<R, T>(reader: R) -> Result<T, serde_json::Error>
35where
36    R: std::io::Read,
37    DicomJson<T>: DeserializeOwned,
38{
39    serde_json::from_reader::<_, DicomJson<T>>(reader).map(DicomJson::into_inner)
40}
41
42/// Deserialize a piece of DICOM data from a serde JSON value.
43pub fn from_value<T>(value: serde_json::Value) -> Result<T, serde_json::Error>
44where
45    DicomJson<T>: DeserializeOwned,
46{
47    serde_json::from_value::<DicomJson<T>>(value).map(DicomJson::into_inner)
48}
49
50#[derive(Debug)]
51struct InMemDicomObjectVisitor<D>(PhantomData<D>);
52
53impl<D> Default for InMemDicomObjectVisitor<D> {
54    fn default() -> Self {
55        Self(PhantomData)
56    }
57}
58
59impl<'de, D> Visitor<'de> for InMemDicomObjectVisitor<D>
60where
61    D: Default + DataDictionary + Clone,
62{
63    type Value = InMemDicomObject<D>;
64
65    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
66        formatter.write_str("a DICOM data set map")
67    }
68
69    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
70    where
71        A: serde::de::MapAccess<'de>,
72    {
73        let mut obj = InMemDicomObject::<D>::new_empty_with_dict(D::default());
74        while let Some(e) = map.next_entry::<DicomJson<Tag>, JsonDataElement<D>>()? {
75            let (
76                DicomJson(tag),
77                JsonDataElement {
78                    vr,
79                    value,
80                    bulk_data_uri,
81                },
82            ) = e;
83            if bulk_data_uri.is_some() {
84                tracing::warn!(
85                    "bulk data URI is not supported for InMemDicomObject; skipping {}",
86                    tag
87                );
88            } else {
89                obj.put(DataElement::new(tag, vr, value));
90            }
91        }
92        Ok(obj)
93    }
94}
95
96impl<'de, I> Deserialize<'de> for DicomJson<InMemDicomObject<I>>
97where
98    I: Default + Clone + DataDictionary,
99{
100    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
101    where
102        D: serde::Deserializer<'de>,
103    {
104        deserializer
105            .deserialize_map(InMemDicomObjectVisitor::default())
106            .map(DicomJson::from)
107    }
108}
109
110#[derive(Debug)]
111struct JsonDataElement<D> {
112    vr: VR,
113    value: Value<InMemDicomObject<D>, InMemFragment>,
114    // TODO(#470): we just ignore this when deserializing with
115    // DicomJson<InMemDicomObject>
116    // Handle this properly with a custom deserializer
117    bulk_data_uri: Option<BulkDataUri>,
118}
119
120#[derive(Debug)]
121struct DataElementVisitor<D>(PhantomData<D>);
122
123impl<D> Default for DataElementVisitor<D> {
124    fn default() -> Self {
125        Self(PhantomData)
126    }
127}
128
129impl<'de, D> Visitor<'de> for DataElementVisitor<D>
130where
131    D: Default + Clone + DataDictionary,
132{
133    type Value = JsonDataElement<D>;
134
135    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
136        formatter.write_str("a data element object")
137    }
138
139    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
140    where
141        A: serde::de::MapAccess<'de>,
142    {
143        let mut values: Option<_> = None;
144        let mut vr = None;
145        let mut value: Option<serde_json::Value> = None;
146        let mut inline_binary = None;
147        let mut bulk_data_uri = None;
148
149        while let Some(key) = map.next_key::<String>()? {
150            match &*key {
151                "vr" => {
152                    if vr.is_some() {
153                        return Err(A::Error::custom("\"vr\" should only be set once"));
154                    }
155
156                    let val: String = map.next_value()?;
157                    vr = Some(VR::from_str(&val).unwrap_or(VR::UN));
158                }
159                "Value" => {
160                    if inline_binary.is_some() {
161                        return Err(A::Error::custom(
162                            "\"Value\" conflicts with \"InlineBinary\"",
163                        ));
164                    }
165
166                    if bulk_data_uri.is_some() {
167                        return Err(A::Error::custom("\"Value\" conflicts with \"BulkDataURI\""));
168                    }
169
170                    value = Some(map.next_value()?);
171                }
172                "InlineBinary" => {
173                    if values.is_some() {
174                        return Err(A::Error::custom(
175                            "\"InlineBinary\" conflicts with \"Value\"",
176                        ));
177                    }
178
179                    if bulk_data_uri.is_some() {
180                        return Err(A::Error::custom(
181                            "\"InlineBinary\" conflicts with \"BulkDataURI\"",
182                        ));
183                    }
184                    // read value as string
185                    let val: String = map.next_value()?;
186                    inline_binary = Some(val);
187                }
188                "BulkDataURI" => {
189                    if values.is_some() {
190                        return Err(A::Error::custom("\"BulkDataURI\" conflicts with \"Value\""));
191                    }
192
193                    if inline_binary.is_some() {
194                        return Err(A::Error::custom(
195                            "\"BulkDataURI\" conflicts with \"InlineBinary\"",
196                        ));
197                    }
198
199                    // read value as string
200                    let val: BulkDataUri = map.next_value()?;
201                    bulk_data_uri = Some(val);
202                }
203                _ => {
204                    return Err(A::Error::custom("Unrecognized data element field"));
205                }
206            }
207        }
208
209        // ensure that VR is present
210        let Some(vr) = vr else {
211            return Err(A::Error::custom("missing VR field"));
212        };
213
214        if let Some(value) = value {
215            // deserialize value in different ways
216            // depending on VR
217            match vr {
218                // sequence
219                VR::SQ => {
220                    let items: Vec<DicomJson<InMemDicomObject<D>>> =
221                        serde_json::from_value(value).map_err(A::Error::custom)?;
222                    let items: Vec<_> = items.into_iter().map(DicomJson::into_inner).collect();
223                    values = Some(Value::Sequence(items.into()));
224                }
225                // always text
226                VR::AE
227                | VR::AS
228                | VR::CS
229                | VR::DA
230                | VR::DT
231                | VR::LO
232                | VR::LT
233                | VR::SH
234                | VR::ST
235                | VR::UT
236                | VR::UR
237                | VR::TM
238                | VR::UC
239                | VR::UI => {
240                    let items: Vec<Option<String>> =
241                        serde_json::from_value(value).map_err(A::Error::custom)?;
242                    let items: Vec<String> =
243                        items.into_iter().map(|v| v.unwrap_or_default()).collect();
244                    values = Some(PrimitiveValue::Strs(items.into()).into());
245                }
246
247                // should always be signed 16-bit integers
248                VR::SS => {
249                    let items: Vec<i16> =
250                        serde_json::from_value(value).map_err(A::Error::custom)?;
251                    values = Some(PrimitiveValue::I16(items.into()).into());
252                }
253                // should always be unsigned 16-bit integers
254                VR::US | VR::OW => {
255                    let items: Vec<u16> =
256                        serde_json::from_value(value).map_err(A::Error::custom)?;
257                    values = Some(PrimitiveValue::U16(items.into()).into());
258                }
259                // should always be signed 32-bit integers
260                VR::SL => {
261                    let items: Vec<i32> =
262                        serde_json::from_value(value).map_err(A::Error::custom)?;
263                    values = Some(PrimitiveValue::I32(items.into()).into());
264                }
265                VR::OB => {
266                    let items: Vec<u8> = serde_json::from_value(value).map_err(A::Error::custom)?;
267                    values = Some(PrimitiveValue::U8(items.into()).into());
268                }
269                // sometimes numbers, sometimes text,
270                // should parse on the spot
271                VR::FL | VR::OF => {
272                    let items: Vec<NumberOrText<f32>> =
273                        serde_json::from_value(value).map_err(A::Error::custom)?;
274                    let items: C<f32> = items
275                        .into_iter()
276                        .map(|v| v.to_num())
277                        .collect::<Result<C<f32>, _>>()
278                        .map_err(A::Error::custom)?;
279                    values = Some(PrimitiveValue::F32(items).into());
280                }
281                VR::FD | VR::OD => {
282                    let items: Vec<NumberOrText<f64>> =
283                        serde_json::from_value(value).map_err(A::Error::custom)?;
284                    let items: C<f64> = items
285                        .into_iter()
286                        .map(|v| v.to_num())
287                        .collect::<Result<C<f64>, _>>()
288                        .map_err(A::Error::custom)?;
289                    values = Some(PrimitiveValue::F64(items).into());
290                }
291                VR::SV => {
292                    let items: Vec<NumberOrText<i64>> =
293                        serde_json::from_value(value).map_err(A::Error::custom)?;
294                    let items: C<i64> = items
295                        .into_iter()
296                        .map(|v| v.to_num())
297                        .collect::<Result<C<i64>, _>>()
298                        .map_err(A::Error::custom)?;
299                    values = Some(PrimitiveValue::I64(items).into());
300                }
301                VR::UL | VR::OL => {
302                    let items: Vec<NumberOrText<u32>> =
303                        serde_json::from_value(value).map_err(A::Error::custom)?;
304                    let items: C<u32> = items
305                        .into_iter()
306                        .map(|v| v.to_num())
307                        .collect::<Result<C<u32>, _>>()
308                        .map_err(A::Error::custom)?;
309                    values = Some(PrimitiveValue::U32(items).into());
310                }
311                VR::UV | VR::OV => {
312                    let items: Vec<NumberOrText<u64>> =
313                        serde_json::from_value(value).map_err(A::Error::custom)?;
314                    let items: C<u64> = items
315                        .into_iter()
316                        .map(|v| v.to_num())
317                        .collect::<Result<C<u64>, _>>()
318                        .map_err(A::Error::custom)?;
319                    values = Some(PrimitiveValue::U64(items).into());
320                }
321                // sometimes numbers, sometimes text,
322                // but retain string form
323                VR::DS => {
324                    let items: Vec<NumberOrText<f64>> =
325                        serde_json::from_value(value).map_err(A::Error::custom)?;
326                    let items: C<String> = items.into_iter().map(|v| v.to_string()).collect();
327                    values = Some(PrimitiveValue::Strs(items).into());
328                }
329                VR::IS => {
330                    let items: Vec<NumberOrText<f64>> =
331                        serde_json::from_value(value).map_err(A::Error::custom)?;
332                    let items: C<String> = items.into_iter().map(|v| v.to_string()).collect();
333                    values = Some(PrimitiveValue::Strs(items).into());
334                }
335                // person names
336                VR::PN => {
337                    let items: Vec<DicomJsonPerson> =
338                        serde_json::from_value(value).map_err(A::Error::custom)?;
339                    let items: C<String> = items.into_iter().map(|v| v.to_string()).collect();
340                    values = Some(PrimitiveValue::Strs(items).into());
341                }
342                // tags
343                VR::AT => {
344                    let items: Vec<DicomJson<Tag>> =
345                        serde_json::from_value(value).map_err(A::Error::custom)?;
346                    let items: C<Tag> = items.into_iter().map(DicomJson::into_inner).collect();
347                    values = Some(PrimitiveValue::Tags(items).into());
348                }
349                // unknown
350                VR::UN => return Err(A::Error::custom("can't parse JSON Value in UN")),
351            }
352        }
353
354        let value = match (values, inline_binary) {
355            (None, None) => PrimitiveValue::Empty.into(),
356            (None, Some(inline_binary)) => {
357                // decode from Base64
358                use base64::Engine;
359                let data = base64::engine::general_purpose::STANDARD
360                    .decode(inline_binary)
361                    .map_err(|_| A::Error::custom("inline binary data is not valid base64"))?;
362                PrimitiveValue::from(data).into()
363            }
364            (Some(values), None) => values,
365            _ => unreachable!(),
366        };
367
368        Ok(JsonDataElement {
369            vr,
370            value,
371            bulk_data_uri,
372        })
373    }
374}
375
376impl<'de, I> Deserialize<'de> for JsonDataElement<I>
377where
378    I: Default + Clone + DataDictionary,
379{
380    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
381    where
382        D: serde::Deserializer<'de>,
383    {
384        deserializer.deserialize_struct(
385            "DataElement",
386            &["vr", "Value", "InlineData", "BulkDataURI"],
387            DataElementVisitor(PhantomData),
388        )
389    }
390}
391
392#[derive(Debug)]
393struct TagVisitor;
394
395impl Visitor<'_> for TagVisitor {
396    type Value = Tag;
397
398    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
399        formatter.write_str("a DICOM tag string in the form \"GGGGEEEE\"")
400    }
401
402    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
403    where
404        E: serde::de::Error,
405    {
406        v.parse().map_err(E::custom)
407    }
408}
409
410impl<'de> Deserialize<'de> for DicomJson<Tag> {
411    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
412    where
413        D: serde::Deserializer<'de>,
414    {
415        deserializer.deserialize_str(TagVisitor).map(DicomJson)
416    }
417}
418
419#[cfg(test)]
420mod tests {
421    use super::from_str;
422    use dicom_core::{dicom_value, DataElement, Tag, VR};
423    use dicom_object::InMemDicomObject;
424    use num_traits::Float;
425
426    /// This asserts that two float slices are equal in size and content.
427    /// It needs a special comparison for NAN values since assert_eq will not match.
428    fn assert_float_slice_eq<T: Float>(actual: &[T], expected: &[T]) {
429        assert_eq!(actual.len(), expected.len());
430        assert!(actual
431            .iter()
432            .zip(actual.iter())
433            .all(|(&a, &b)| (a == b) || (a.is_nan() && b.is_nan())));
434    }
435
436    #[test]
437    fn can_parse_tags() {
438        let serialized = "\"00080010\"";
439        let tag: Tag = from_str(serialized).unwrap();
440        assert_eq!(tag, Tag(0x0008, 0x0010));
441
442        let serialized = "\"00200013\"";
443        let tag: Tag = from_str(serialized).unwrap();
444        assert_eq!(tag, Tag(0x0020, 0x0013));
445    }
446
447    #[test]
448    fn can_parse_simple_data_sets() {
449        let serialized = serde_json::json!({
450            "00080005": {
451                "Value": [ "ISO_IR 192" ],
452                "vr": "CS"
453            },
454            "00080020": {
455                "vr": "DA",
456                "Value": [ "20130409" ]
457            },
458            "00080061": {
459                "vr": "CS",
460                "Value": [
461                    "CT",
462                    "PET"
463                ]
464            },
465            "00080090": {
466                "vr": "PN",
467                "Value": [
468                  {
469                    "Alphabetic": "^Bob^^Dr."
470                  }
471                ]
472            },
473            "00091002": {
474                "vr": "UN",
475                "InlineBinary": "z0x9c8v7"
476            },
477            "00101010": {
478                "vr": "AS",
479                "Value": [ "30Y" ]
480            }
481        });
482
483        let obj: InMemDicomObject = super::from_value(serialized).unwrap();
484
485        let tag = Tag(0x0008, 0x0005);
486        assert_eq!(
487            obj.get(tag),
488            Some(&DataElement::new(tag, VR::CS, "ISO_IR 192")),
489        )
490    }
491
492    #[test]
493    fn can_parse_null_values() {
494        let serialized = serde_json::json!({
495            "00080008": {
496                "Value": [
497                  "DERIVED",
498                  "PRIMARY",
499                  "POST_PROCESSED",
500                  "RT",
501                  null,
502                  null,
503                  null,
504                  null,
505                  "100000"
506                ],
507                "vr": "CS"
508              }
509        });
510
511        let obj: InMemDicomObject = super::from_value(serialized).unwrap();
512
513        let tag = Tag(0x0008, 0x0008);
514        assert_eq!(
515            obj.get(tag),
516            Some(&DataElement::new(
517                tag,
518                VR::CS,
519                dicom_value!(
520                    Strs,
521                    [
522                        "DERIVED",
523                        "PRIMARY",
524                        "POST_PROCESSED",
525                        "RT",
526                        "",
527                        "",
528                        "",
529                        "",
530                        "100000",
531                    ]
532                )
533            )),
534        )
535    }
536
537    #[test]
538    fn can_resolve_bulk_data() {
539        let serialized = serde_json::json!({
540            "7FE00010": {
541                "vr": "OW",
542                "BulkDataURI": "http://localhost:8042/dicom-web/studies/1.2.276.0.89.300.10035584652.20181014.93645/series/1.2.392.200036.9125.3.1696751121028.64888163108.42362060/instances/1.2.392.200036.9125.9.0.454007928.539582480.1883970570/bulk/7fe00010"
543            }
544        });
545
546        assert!(super::from_value::<InMemDicomObject>(serialized).is_ok());
547    }
548
549    #[test]
550    fn can_resolve_nan_and_inf_float() {
551        let serialized = serde_json::json!({
552            "0018605A": {
553                "vr": "FL",
554                "Value": [
555                    5492.8545,
556                    5462.5205,
557                    "NaN",
558                    "-inf",
559                    "inf"
560                ]
561            }
562        });
563
564        let obj: InMemDicomObject = super::from_value(serialized).unwrap();
565        let tag = Tag(0x0018, 0x605A);
566        let element = obj.get(tag).unwrap();
567
568        // verify NAN, INFINITY, and NEG_INFINITY are correctly deserialized to f32::NAN, f32::INFINITY, and f32::NEG_INFINITY
569        let actual_values = element.float32_slice().unwrap();
570        let expected_values = &[
571            5492.8545,
572            5462.5205,
573            f32::NAN,
574            f32::NEG_INFINITY,
575            f32::INFINITY,
576        ];
577
578        assert_float_slice_eq(actual_values, expected_values);
579
580        // validate upcasting to float 64, additional precision (5492.8544921875) is expected beyond original (5492.8545) due to upcasting
581        let actual_values_multifloat_64 = element.to_multi_float64().unwrap();
582        let expected_values_multifloat_64 = &[
583            5492.8544921875,
584            5462.5205078125,
585            f64::NAN,
586            f64::NEG_INFINITY,
587            f64::INFINITY,
588        ];
589
590        assert_float_slice_eq(&actual_values_multifloat_64, expected_values_multifloat_64);
591    }
592}