Skip to main content

opcua_types/node_id/
json.rs

1use std::io::{Read, Write};
2use std::str::FromStr;
3
4use tracing::warn;
5
6use super::{Identifier, NodeId};
7use crate::{json::*, ByteString, Error, Guid, UAString};
8
9// JSON serialization schema as per spec:
10//
11// "Type"
12//      The IdentifierType encoded as a JSON number.
13//      Allowed values are:
14//            0 - UInt32 Identifier encoded as a JSON number.
15//            1 - A String Identifier encoded as a JSON string.
16//            2 - A Guid Identifier encoded as described in 5.4.2.7.
17//            3 - A ByteString Identifier encoded as described in 5.4.2.8.
18//      This field is omitted for UInt32 identifiers.
19// "Id"
20//      The Identifier.
21//      The value of the id field specifies the encoding of this field.
22// "Namespace"
23//      The NamespaceIndex for the NodeId.
24//      The field is encoded as a JSON number for the reversible encoding.
25//      The field is omitted if the NamespaceIndex equals 0.
26//      For the non-reversible encoding, the field is the NamespaceUri associated with the NamespaceIndex, encoded as a JSON string.
27//      A NamespaceIndex of 1 is always encoded as a JSON number.
28
29enum RawIdentifier {
30    String(String),
31    Integer(u32),
32}
33
34impl JsonEncodable for NodeId {
35    fn encode(
36        &self,
37        stream: &mut JsonStreamWriter<&mut dyn Write>,
38        ctx: &crate::json::Context<'_>,
39    ) -> crate::EncodingResult<()> {
40        stream.begin_object()?;
41        match &self.identifier {
42            super::Identifier::Numeric(n) => {
43                stream.name("Id")?;
44                stream.number_value(*n)?;
45            }
46            super::Identifier::String(uastring) => {
47                stream.name("IdType")?;
48                stream.number_value(1)?;
49                stream.name("Id")?;
50                JsonEncodable::encode(uastring, stream, ctx)?;
51            }
52            super::Identifier::Guid(guid) => {
53                stream.name("IdType")?;
54                stream.number_value(2)?;
55                stream.name("Id")?;
56                JsonEncodable::encode(guid, stream, ctx)?;
57            }
58            super::Identifier::ByteString(byte_string) => {
59                stream.name("IdType")?;
60                stream.number_value(3)?;
61                stream.name("Id")?;
62                JsonEncodable::encode(byte_string, stream, ctx)?;
63            }
64        }
65        if self.namespace != 0 {
66            stream.name("Namespace")?;
67            stream.number_value(self.namespace)?;
68        }
69        stream.end_object()?;
70        Ok(())
71    }
72}
73
74impl JsonDecodable for NodeId {
75    fn decode(
76        stream: &mut JsonStreamReader<&mut dyn Read>,
77        _ctx: &Context<'_>,
78    ) -> crate::EncodingResult<Self> {
79        match stream.peek()? {
80            ValueType::Null => {
81                stream.next_null()?;
82                return Ok(Self::null());
83            }
84            _ => stream.begin_object()?,
85        }
86
87        let mut id_type: Option<u16> = None;
88        let mut namespace: Option<u16> = None;
89        let mut value: Option<RawIdentifier> = None;
90
91        while stream.has_next()? {
92            match stream.next_name()? {
93                "IdType" => {
94                    id_type = Some(stream.next_number()??);
95                }
96                "Namespace" => {
97                    namespace = Some(stream.next_number()??);
98                }
99                "Id" => match stream.peek()? {
100                    ValueType::Null => {
101                        stream.next_null()?;
102                        value = Some(RawIdentifier::Integer(0));
103                    }
104                    ValueType::Number => {
105                        value = Some(RawIdentifier::Integer(stream.next_number()??));
106                    }
107                    _ => {
108                        value = Some(RawIdentifier::String(stream.next_string()?));
109                    }
110                },
111                _ => stream.skip_value()?,
112            }
113        }
114
115        let identifier = match id_type {
116            Some(1) => {
117                let Some(RawIdentifier::String(s)) = value else {
118                    return Err(Error::decoding("Invalid NodeId, empty identifier"));
119                };
120                let s = UAString::from(s);
121                if s.is_null() || s.is_empty() {
122                    return Err(Error::decoding("Invalid NodeId, empty identifier"));
123                }
124                Identifier::String(s)
125            }
126            Some(2) => {
127                let Some(RawIdentifier::String(s)) = value else {
128                    return Err(Error::decoding("Invalid NodeId, empty identifier"));
129                };
130                if s.is_empty() {
131                    return Err(Error::decoding("Invalid NodeId, empty identifier"));
132                }
133                let s = Guid::from_str(&s).map_err(|_| {
134                    warn!("Unable to decode GUID identifier");
135                    Error::decoding("Unable to decode GUID identifier")
136                })?;
137                Identifier::Guid(s)
138            }
139            Some(3) => {
140                let Some(RawIdentifier::String(s)) = value else {
141                    return Err(Error::decoding("Invalid NodeId, empty identifier"));
142                };
143                if s.is_empty() {
144                    return Err(Error::decoding("Invalid NodeId, empty identifier"));
145                }
146                let s: ByteString = ByteString::from_base64(&s)
147                    .ok_or_else(|| Error::decoding("Unable to decode bytestring identifier"))?;
148                Identifier::ByteString(s)
149            }
150            None | Some(0) => {
151                let Some(RawIdentifier::Integer(s)) = value else {
152                    return Err(Error::decoding("Invalid NodeId, empty identifier"));
153                };
154                Identifier::Numeric(s)
155            }
156            Some(r) => {
157                return Err(Error::decoding(format!(
158                    "Failed to deserialize NodeId, got unexpected IdType {r}"
159                )));
160            }
161        };
162
163        stream.end_object()?;
164        Ok(Self {
165            namespace: namespace.unwrap_or_default(),
166            identifier,
167        })
168    }
169}