Skip to main content

opcua/types/
variant_json.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2022 Adam Lock
4
5//! Implements the JSON serialization functionality for Variant. OPC UA serialization over JSON is
6//! described by Part 6 of the spec in not a whole lot of detail, so this implementation tries its best to implement
7//! it as described. In some places there may be ambiguity, e.g. whether a null field corresponds to
8//! null in one of the types. In addition, serialization of JSON is so different than the binary form
9//! that it doesn't share any code with OPC UA binary.
10//!
11//! Serialization is implemented as REVERSIBLE, i.e. the format written out can be used to deserialize back
12//! to the original type. In addition, reversible means some data such as `StatusCode`, `NodeId` and
13//! `ExpandedNodeId` is written a certain way.
14//!
15//! To distinguish between binary and JSON, the built-in types implement Serde's `Serialize`/`Deserialize`
16//! traits for JSON serialization.
17
18use std::{fmt, i32};
19
20use serde::{
21    de, de::DeserializeOwned, de::Error, Deserialize, Deserializer, Serialize, Serializer,
22};
23use serde_json::json;
24
25use crate::types::*;
26
27/// This enum represents the scalar "Type" used for JSON serializing of variants as defined in Part 6 5.1.2.
28///
29/// It is almost but it not the same as the DataTypeId
30///
31/// 1 	Boolean 	A two-state logical value (true or false).
32/// 2 	SByte 	An integer value between −128 and 127 inclusive.
33/// 3 	Byte 	An integer value between 0 and 255 inclusive.
34/// 4 	Int16 	An integer value between −32 768 and 32 767 inclusive.
35/// 5 	UInt16 	An integer value between 0 and 65 535 inclusive.
36/// 6 	Int32 	An integer value between −2 147 483 648 and 2 147 483 647 inclusive.
37/// 7 	UInt32 	An integer value between 0 and 4 294 967 295 inclusive.
38/// 8 	Int64 	An integer value between −9 223 372 036 854 775 808 and 9 223 372 036 854 775 807 inclusive.
39/// 9 	UInt64 	An integer value between 0 and 18 446 744 073 709 551 615 inclusive.
40/// 10 	Float 	An IEEE single precision (32 bit) floating point value.
41/// 11 	Double 	An IEEE double precision (64 bit) floating point value.
42/// 12 	String 	A sequence of Unicode characters.
43/// 13 	DateTime 	An instance in time.
44/// 14 	Guid 	A 16-byte value that can be used as a globally unique identifier.
45/// 15 	ByteString 	A sequence of octets.
46/// 16 	XmlElement 	An XML element.
47/// 17 	NodeId 	An identifier for a node in the address space of an OPC UA Server.
48/// 18 	ExpandedNodeId 	A NodeId that allows the namespace URI to be specified instead of an index.
49/// 19 	StatusCode 	A numeric identifier for an error or condition that is associated with a value or an operation.
50/// 20 	QualifiedName 	A name qualified by a namespace.
51/// 21 	LocalizedText 	Human readable text with an optional locale identifier.
52/// 22 	ExtensionObject 	A structure that contains an application specific data type that may not be recognized by the receiver.
53/// 23 	DataValue 	A data value with an associated status code and timestamps.
54/// 24 	Variant 	A union of all of the types specified above.
55/// 25 	DiagnosticInfo 	A structure that contains detailed error and diagnostic information associated with a StatusCode.
56///
57pub(crate) enum VariantJsonId {
58    Empty = 0,
59    Boolean,
60    SByte,
61    Byte,
62    Int16,
63    UInt16,
64    Int32,
65    UInt32,
66    Int64,
67    UInt64,
68    Float,
69    Double,
70    String,
71    DateTime,
72    Guid,
73    ByteString,
74    XmlElement,
75    NodeId,
76    ExpandedNodeId,
77    StatusCode,
78    QualifiedName,
79    LocalizedText,
80    ExtensionObject,
81    DataValue,
82    Variant,
83    DiagnosticInfo,
84}
85
86/// This is the JSON representation of the Variant. We use the struct to benefit from the derived implementations of
87/// Serialize / Deserialize that cut out a lot of manually written code. There is an outer Serialize/Deserialize on
88/// Variant that is responsible for preparing the struct for writing and validating it after reading.
89#[derive(Serialize, Deserialize)]
90struct JsonVariant {
91    #[serde(rename = "Type")]
92    variant_type: u32,
93    #[serde(skip_serializing_if = "Option::is_none")]
94    #[serde(rename = "Body")]
95    body: Option<serde_json::Value>,
96    #[serde(skip_serializing_if = "Option::is_none")]
97    #[serde(rename = "Dimensions")]
98    dimensions: Option<Vec<u32>>,
99}
100
101const VALUE_INFINITY: &str = "Infinity";
102const VALUE_NEG_INFINITY: &str = "-Infinity";
103const VALUE_NAN: &str = "NaN";
104
105/// Turns a float or double to json, including special case values
106macro_rules! float_to_json {
107    ( $v: expr, $t: ty) => {
108        if $v == <$t>::INFINITY {
109            json!(VALUE_INFINITY)
110        } else if $v == <$t>::NEG_INFINITY {
111            json!(VALUE_NEG_INFINITY)
112        } else if $v.is_nan() {
113            json!(VALUE_NAN)
114        } else {
115            json!($v)
116        }
117    };
118}
119
120/// Turns a serializable struct into JSON
121macro_rules! serializable_to_json {
122    ( $v: expr ) => {
123        serde_json::value::to_value($v).unwrap()
124    };
125}
126
127/// Deserialize the json as the value type
128fn json_as_value<T, E>(v: Option<serde_json::Value>, typename: &str) -> Result<T, E>
129where
130    T: DeserializeOwned,
131    E: Error,
132{
133    if let Some(v) = v {
134        let v = serde_json::from_value::<T>(v)
135            .map_err(|_| Error::custom(format!("Invalid value, cannot parse {}", typename)))?;
136        Ok(v)
137    } else {
138        Err(Error::custom(format!(
139            "Invalid value, cannot parse {}",
140            typename
141        )))
142    }
143}
144
145// Implement Serialize / Deserialize as per https://reference.opcfoundation.org/v104/Core/docs/Part6/5.4.2/
146//
147// Reversible json requires this info
148//
149// {
150//   "Type": 0 for NULL, or other enum
151//   "Body": scalar, object or array according to type
152//   "Dimensions": dimensions of array for multi-dimensional arrays only
153// }
154//
155// Non reversible requires just the body value.
156impl Serialize for Variant {
157    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
158    where
159        S: Serializer,
160    {
161        // Write body
162        let (body, dimensions) = if *self == Variant::Empty {
163            (None, None)
164        } else {
165            let body = match self {
166                // Boolean as json true or false
167                Variant::Boolean(v) => json!(*v),
168                // Integers except 64-bit variants as json numbers
169                Variant::SByte(v) => json!(*v),
170                Variant::Byte(v) => json!(*v),
171                Variant::Int16(v) => json!(*v),
172                Variant::UInt16(v) => json!(*v),
173                Variant::Int32(v) => json!(*v),
174                Variant::UInt32(v) => json!(*v),
175                // Integers 64-bit as strings
176                Variant::Int64(v) => json!(v.to_string()),
177                Variant::UInt64(v) => json!(v.to_string()),
178                // Float/double as json numbers. Strings used for special cases. Note that v is not matched to
179                // f32/f64::NAN since IEE754 docs says various bit patterns can be NaN.
180                Variant::Float(v) => float_to_json!(*v, f32),
181                Variant::Double(v) => float_to_json!(*v, f64),
182                // String as json strings - does not say what to do for null
183                Variant::String(v) => serializable_to_json!(v),
184                // XmlElement as string
185                Variant::XmlElement(v) => serializable_to_json!(v),
186                // Datetime as ISO 8601:2004 string, limited and trimmed within “0001-01-01T00:00:00Z” or “9999-12-31T23:59:59Z” range
187                Variant::DateTime(v) => serializable_to_json!(v),
188                // Guid as string in format C496578A-0DFE-4B8F-870A-745238C6AEAE
189                Variant::Guid(v) => serializable_to_json!(v),
190                // Bytestring as base64 encoded string
191                Variant::ByteString(v) => serializable_to_json!(v),
192                // NodeId as object - { IdType=[0123], Id=value, Namespace=3 }
193                Variant::NodeId(v) => serializable_to_json!(v),
194                // ExpandedNodeId
195                Variant::ExpandedNodeId(v) => serializable_to_json!(v),
196                // StatusCode as a number
197                Variant::StatusCode(v) => serializable_to_json!(v),
198                // QualifiedName as object { Name="name", Uri="uri" }. See 5.4.2.14
199                Variant::QualifiedName(v) => serializable_to_json!(v),
200                // LocalizedText as object { Locale="locale", Text="text" }
201                Variant::LocalizedText(v) => serializable_to_json!(v),
202                // ExtensionObject as object { TypeId=nodeid, Encoding=[012], Body="data"}, see 5.4.2.16
203                Variant::ExtensionObject(v) => serializable_to_json!(v),
204                // DataValue as object { Value=variant, Status=statuscode, SourceTimestamp=DateTime, SourcePicoSeconds=Uint16 etc.}
205                Variant::DataValue(v) => serializable_to_json!(v),
206                // DiagnosticInfo - see 5.4.2.13
207                Variant::DiagnosticInfo(v) => serializable_to_json!(v),
208                // Variant
209                Variant::Variant(v) => serializable_to_json!(v),
210                /*
211                Variant::Array(_array) => {
212                    // TODO serialize the values in an array
213                    // TODO get array dimensions and serialize in an array
214                    // json_variant.dimensions = Some(dimensions)
215                    todo!();
216                }
217                */
218                _ => panic!("Unsupported variant type"),
219            };
220            (Some(body), None)
221        };
222
223        let json_variant = JsonVariant {
224            variant_type: self.json_id() as u32,
225            body,
226            dimensions,
227        };
228        json_variant.serialize(serializer)
229    }
230}
231
232struct VariantVisitor;
233
234impl VariantVisitor {
235    /// Extracts a signed value integer out of the JSON value or 0
236    fn numeric_i64<E>(
237        v: Option<serde_json::Value>,
238        name: &str,
239        min: i64,
240        max: i64,
241    ) -> Result<i64, E>
242    where
243        E: de::Error,
244    {
245        if let Some(v) = v {
246            let v = v
247                .as_i64()
248                .ok_or_else(|| Error::custom(format!("Wrong type, expecting {} value", name)))?;
249            if v < min || v > max {
250                Err(Error::custom(format!(
251                    "Value {} is out of range for {}",
252                    v, name
253                )))
254            } else {
255                Ok(v)
256            }
257        } else {
258            Ok(0)
259        }
260    }
261
262    /// Extracts an unsigned value integer out of the JSON value or 0
263    fn numeric_u64<E>(
264        v: Option<serde_json::Value>,
265        name: &str,
266        min: u64,
267        max: u64,
268    ) -> Result<u64, E>
269    where
270        E: de::Error,
271    {
272        if let Some(v) = v {
273            let v = v
274                .as_u64()
275                .ok_or_else(|| Error::custom(format!("Wrong type, expecting {} value", name)))?;
276            if v < min || v > max {
277                Err(Error::custom(format!(
278                    "Value {} is out of range for {}",
279                    v, name
280                )))
281            } else {
282                Ok(v)
283            }
284        } else {
285            Ok(0)
286        }
287    }
288
289    /// Extracts a double precision floating point number out of the JSON value or 0
290    fn numeric_f64<E>(
291        v: Option<serde_json::Value>,
292        name: &str,
293        min: f64,
294        max: f64,
295    ) -> Result<f64, E>
296    where
297        E: de::Error,
298    {
299        if let Some(v) = v {
300            // Special case values for floating point values
301            if let Some(v) = v.as_str() {
302                match v {
303                    VALUE_INFINITY => Ok(f64::INFINITY),
304                    VALUE_NEG_INFINITY => Ok(f64::NEG_INFINITY),
305                    VALUE_NAN => Ok(f64::NAN),
306                    _ => Err(Error::custom(format!(
307                        "Wrong type, expecting {} value",
308                        name
309                    ))),
310                }
311            } else {
312                let v = v.as_f64().ok_or_else(|| {
313                    Error::custom(format!("Wrong type, expecting {} value", name))
314                })?;
315                if v < min || v > max {
316                    Err(Error::custom(format!(
317                        "Value {} is out of range for {}",
318                        v, name
319                    )))
320                } else {
321                    Ok(v)
322                }
323            }
324        } else {
325            Ok(0.0)
326        }
327    }
328}
329
330impl<'de> serde::de::Visitor<'de> for VariantVisitor {
331    type Value = Variant;
332
333    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
334        write!(formatter, "a variant value or null")
335    }
336
337    fn visit_none<E>(self) -> Result<Self::Value, E>
338    where
339        E: de::Error,
340    {
341        Ok(Variant::Empty)
342    }
343
344    fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
345    where
346        D: Deserializer<'de>,
347    {
348        let v = JsonVariant::deserialize(deserializer)?;
349
350        let t = v.variant_type;
351        let body = v.body;
352        let dimensions = v.dimensions;
353
354        if dimensions.is_some() {
355            // TODO support arrays of values
356            return Err(Error::custom("Dimensions not supported yet"));
357        }
358
359        match t {
360            t if t == VariantJsonId::Empty as u32 => {
361                if body.is_some() {
362                    Err(Error::custom("Unexpected Body"))
363                } else {
364                    Ok(Variant::Empty)
365                }
366            }
367            // Boolean
368            t if t == VariantJsonId::Boolean as u32 => {
369                let v = body.ok_or_else(|| Error::custom("Missing Boolean value"))?;
370                Ok(Variant::Boolean(
371                    v.as_bool()
372                        .ok_or_else(|| Error::custom("Value is not Boolean"))?,
373                ))
374            }
375            // Numerics
376            t if t == VariantJsonId::SByte as u32 => {
377                Ok(Variant::SByte(
378                    Self::numeric_i64(body, "SByte", i8::MIN as i64, i8::MAX as i64)? as i8,
379                ))
380            }
381            t if t == VariantJsonId::Byte as u32 => {
382                Ok(Variant::Byte(
383                    Self::numeric_u64(body, "Byte", u8::MIN as u64, u8::MAX as u64)? as u8,
384                ))
385            }
386            t if t == VariantJsonId::Int16 as u32 => Ok(Variant::Int16(Self::numeric_i64(
387                body,
388                "Int16",
389                i16::MIN as i64,
390                i16::MAX as i64,
391            )? as i16)),
392            t if t == VariantJsonId::UInt16 as u32 => Ok(Variant::UInt16(Self::numeric_u64(
393                body,
394                "UInt16",
395                u16::MIN as u64,
396                u16::MAX as u64,
397            )? as u16)),
398            t if t == VariantJsonId::Int32 as u32 => Ok(Variant::Int32(Self::numeric_i64(
399                body,
400                "Int32",
401                i32::MIN as i64,
402                i32::MAX as i64,
403            )? as i32)),
404            t if t == VariantJsonId::UInt32 as u32 => Ok(Variant::UInt32(Self::numeric_u64(
405                body,
406                "UInt32",
407                u32::MIN as u64,
408                u32::MAX as u64,
409            )? as u32)),
410            t if t == VariantJsonId::Int64 as u32 => {
411                let v = if let Some(v) = body {
412                    const ERR: &str = "Int64 encoded as a string";
413                    let v = v.as_str().ok_or_else(|| {
414                        Error::custom(format!("Wrong type, expecting {} value", ERR))
415                    })?;
416                    v.parse::<i64>().map_err(|_| {
417                        Error::custom(format!("Parse error, expecting {} value", ERR))
418                    })?
419                } else {
420                    0i64
421                };
422                Ok(Variant::Int64(v))
423            }
424            t if t == VariantJsonId::UInt64 as u32 => {
425                let v = if let Some(v) = body {
426                    const ERR: &str = "UInt64 encoded as a string";
427                    let v = v.as_str().ok_or_else(|| {
428                        Error::custom(format!("Wrong type, expecting {} value", ERR))
429                    })?;
430                    v.parse::<u64>().map_err(|_| {
431                        Error::custom(format!("Parse error, expecting {} value", ERR))
432                    })?
433                } else {
434                    0u64
435                };
436                Ok(Variant::UInt64(v))
437            }
438            t if t == VariantJsonId::Float as u32 => Ok(Variant::Float(Self::numeric_f64(
439                body,
440                "Float",
441                f32::MIN as f64,
442                f32::MAX as f64,
443            )? as f32)),
444            t if t == VariantJsonId::Double as u32 => Ok(Variant::Double(Self::numeric_f64(
445                body,
446                "Double",
447                f64::MIN,
448                f64::MAX,
449            )?)),
450            t if t == VariantJsonId::String as u32 => {
451                if let Some(ref v) = body {
452                    if v.is_null() {
453                        Ok(Variant::String(UAString::null()))
454                    } else {
455                        json_as_value(body, "UAString").map(|v| Variant::String(v))
456                    }
457                } else {
458                    Ok(Variant::String(UAString::null()))
459                }
460            }
461            t if t == VariantJsonId::DateTime as u32 => {
462                json_as_value(body, "DateTime").map(|v| Variant::DateTime(Box::new(v)))
463            }
464            t if t == VariantJsonId::Guid as u32 => {
465                json_as_value(body, "Guid").map(|v| Variant::Guid(Box::new(v)))
466            }
467            t if t == VariantJsonId::ByteString as u32 => {
468                if let Some(ref v) = body {
469                    if v.is_null() {
470                        Ok(Variant::ByteString(ByteString::null()))
471                    } else {
472                        json_as_value(body, "ByteString").map(|v| Variant::ByteString(v))
473                    }
474                } else {
475                    Ok(Variant::ByteString(ByteString::null()))
476                }
477            }
478            t if t == VariantJsonId::XmlElement as u32 => {
479                json_as_value(body, "XmlElement").map(|v| Variant::XmlElement(v))
480            }
481            t if t == VariantJsonId::NodeId as u32 => {
482                json_as_value(body, "NodeId").map(|v| Variant::NodeId(Box::new(v)))
483            }
484            t if t == VariantJsonId::ExpandedNodeId as u32 => {
485                json_as_value(body, "ExpandedNodeId").map(|v| Variant::ExpandedNodeId(Box::new(v)))
486            }
487            t if t == VariantJsonId::StatusCode as u32 => {
488                if let Some(v) = body {
489                    let v = serde_json::from_value::<u32>(v)
490                        .map_err(|_| Error::custom("Invalid value, cannot parse StatusCode"))?;
491                    Ok(Variant::StatusCode(StatusCode::from_bits_truncate(v)))
492                } else {
493                    Err(Error::custom("Invalid value, cannot parse StatusCode"))
494                }
495            }
496            t if t == VariantJsonId::QualifiedName as u32 => {
497                json_as_value(body, "QualifiedName").map(|v| Variant::QualifiedName(Box::new(v)))
498            }
499            t if t == VariantJsonId::LocalizedText as u32 => {
500                json_as_value(body, "LocalizedText").map(|v| Variant::LocalizedText(Box::new(v)))
501            }
502            t if t == VariantJsonId::ExtensionObject as u32 => {
503                json_as_value(body, "ExtensionObject")
504                    .map(|v| Variant::ExtensionObject(Box::new(v)))
505            }
506            t if t == VariantJsonId::DataValue as u32 => {
507                json_as_value(body, "DataValue").map(|v| Variant::DataValue(Box::new(v)))
508            }
509            t if t == VariantJsonId::Variant as u32 => {
510                json_as_value(body, "Variant").map(|v| Variant::Variant(Box::new(v)))
511            }
512            t if t == VariantJsonId::DiagnosticInfo as u32 => {
513                json_as_value(body, "DiagnosticInfo").map(|v| Variant::DiagnosticInfo(Box::new(v)))
514            }
515            t => Err(Error::custom(format!("Unhandled type {}", t))),
516        }
517    }
518}
519
520impl<'de> Deserialize<'de> for Variant {
521    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
522    where
523        D: Deserializer<'de>,
524    {
525        deserializer.deserialize_option(VariantVisitor)
526    }
527}
528
529impl Variant {
530    fn json_id(&self) -> VariantJsonId {
531        match self {
532            Variant::Empty => VariantJsonId::Empty,
533            Variant::Boolean(_) => VariantJsonId::Boolean,
534            Variant::SByte(_) => VariantJsonId::SByte,
535            Variant::Byte(_) => VariantJsonId::Byte,
536            Variant::Int16(_) => VariantJsonId::Int16,
537            Variant::UInt16(_) => VariantJsonId::UInt16,
538            Variant::Int32(_) => VariantJsonId::Int32,
539            Variant::UInt32(_) => VariantJsonId::UInt32,
540            Variant::Int64(_) => VariantJsonId::Int64,
541            Variant::UInt64(_) => VariantJsonId::UInt64,
542            Variant::Float(_) => VariantJsonId::Float,
543            Variant::Double(_) => VariantJsonId::Double,
544            Variant::String(_) => VariantJsonId::String,
545            Variant::DateTime(_) => VariantJsonId::DateTime,
546            Variant::Guid(_) => VariantJsonId::Guid,
547            Variant::ByteString(_) => VariantJsonId::ByteString,
548            Variant::XmlElement(_) => VariantJsonId::XmlElement,
549            Variant::NodeId(_) => VariantJsonId::NodeId,
550            Variant::ExpandedNodeId(_) => VariantJsonId::ExpandedNodeId,
551            Variant::StatusCode(_) => VariantJsonId::StatusCode,
552            Variant::QualifiedName(_) => VariantJsonId::QualifiedName,
553            Variant::LocalizedText(_) => VariantJsonId::LocalizedText,
554            Variant::ExtensionObject(_) => VariantJsonId::ExtensionObject,
555            Variant::Variant(_) => VariantJsonId::Variant,
556            Variant::DataValue(_) => VariantJsonId::DataValue,
557            Variant::DiagnosticInfo(_) => VariantJsonId::DiagnosticInfo,
558            _ => {
559                // TODO for arrays, will have to get the json id for the first element
560                panic!("Cannot return type")
561            }
562        }
563    }
564}