1818import com .google .common .io .BaseEncoding ;
1919import com .google .gson .Gson ;
2020import com .google .gson .GsonBuilder ;
21+ import com .google .gson .JsonArray ;
2122import com .google .gson .JsonElement ;
2223import com .google .gson .JsonObject ;
2324import com .google .gson .JsonPrimitive ;
4950import java .nio .charset .StandardCharsets ;
5051import java .time .Instant ;
5152import java .time .format .DateTimeFormatter ;
52- import java .util .Optional ;
53+ import java .util .Arrays ;
5354import java .util .UUID ;
5455
5556//see also https://reference.opcfoundation.org/Core/Part6/v105/docs/5.4
@@ -67,12 +68,12 @@ public class OpcUaJsonPayloadConverter {
6768 final @ NotNull PollingContext pollingContext ) {
6869 OpcUaJsonPayloadConverter .pollingContext = pollingContext ;
6970 final Object value = dataValue .getValue ().getValue ();
70- final boolean reversibleMode = false ;
7171 final JsonObject jsonObject = new JsonObject ();
72- if (reversibleMode ) {
73- addDataValueFields (dataValue , jsonObject , reversibleMode );
72+
73+ if (value instanceof DataValue ) {
74+ addDataValueFields ((DataValue ) value , jsonObject );
7475 }
75- convertValue ( value , jsonObject , reversibleMode , " value" , serializationContext );
76+ jsonObject . add ( " value" , convertValue ( value , serializationContext ) );
7677
7778 if (pollingContext .getIncludeTimestamp ()) {
7879 jsonObject .addProperty ("timestamp" , System .currentTimeMillis ());
@@ -82,63 +83,56 @@ public class OpcUaJsonPayloadConverter {
8283 jsonObject .addProperty ("tag" , pollingContext .getTagName ());
8384 }
8485
85-
8686 return ByteBuffer .wrap (GSON .toJson (jsonObject ).getBytes (StandardCharsets .UTF_8 ));
8787 }
88-
89- private static void convertValue (
88+
89+ private static JsonElement convertValue (
9090 final @ NotNull Object value ,
91- final @ NotNull JsonObject holder ,
92- final boolean reversibleMode ,
93- final @ NotNull String fieldName ,
9491 final @ NotNull SerializationContext serializationContext ) {
9592 if (value instanceof DataValue ) {
96- addDataValueFields ((DataValue ) value , holder , reversibleMode );
97- convertValue (((DataValue ) value ).getValue (), holder , reversibleMode , fieldName , serializationContext );
93+ return convertValue (((DataValue ) value ).getValue (), serializationContext );
9894 } else if (value instanceof Boolean ) {
99- holder . add ( fieldName , new JsonPrimitive ((Boolean ) value ) );
95+ return new JsonPrimitive ((Boolean ) value );
10096 } else if (value instanceof Byte ) {
101- holder . add ( fieldName , new JsonPrimitive ((Byte ) value ) );
97+ return new JsonPrimitive ((Byte ) value );
10298 } else if (value instanceof UByte ) {
103- holder . add ( fieldName , new JsonPrimitive (((UByte ) value ).intValue () ));
99+ return new JsonPrimitive (((UByte ) value ).intValue ());
104100 } else if (value instanceof Short ) {
105- holder . add ( fieldName , new JsonPrimitive ((Short ) value ) );
101+ return new JsonPrimitive ((Short ) value );
106102 } else if (value instanceof UShort ) {
107- holder . add ( fieldName , new JsonPrimitive (((UShort ) value ).intValue () ));
103+ return new JsonPrimitive (((UShort ) value ).intValue ());
108104 } else if (value instanceof Integer ) {
109- holder . add ( fieldName , new JsonPrimitive (((Integer ) value ) ));
105+ return new JsonPrimitive (((Integer ) value ));
110106 } else if (value instanceof UInteger ) {
111- holder . add ( fieldName , new JsonPrimitive ((((UInteger ) value ).longValue () )));
107+ return new JsonPrimitive ((((UInteger ) value ).longValue ()));
112108 } else if (value instanceof Long ) {
113- holder . add ( fieldName , new JsonPrimitive (((Long ) value ) ));
109+ return new JsonPrimitive (((Long ) value ));
114110 } else if (value instanceof ULong ) {
115- holder . add ( fieldName , new JsonPrimitive ((((ULong ) value ).toBigInteger () )));
111+ return new JsonPrimitive ((((ULong ) value ).toBigInteger ()));
116112 } else if (value instanceof Float ) {
117- holder . add ( fieldName , new JsonPrimitive (((Float ) value ) ));
113+ return new JsonPrimitive (((Float ) value ));
118114 } else if (value instanceof Double ) {
119- holder . add ( fieldName , new JsonPrimitive (((Double ) value ) ));
115+ return new JsonPrimitive (((Double ) value ));
120116 } else if (value instanceof String ) {
121- holder . add ( fieldName , new JsonPrimitive (((String ) value ) ));
117+ return new JsonPrimitive (((String ) value ));
122118 } else if (value instanceof DateTime ) {
123- holder .add (fieldName ,
124- new JsonPrimitive ((DateTimeFormatter .ISO_INSTANT .format (((DateTime ) value ).getJavaInstant ()))));
119+ return new JsonPrimitive ((DateTimeFormatter .ISO_INSTANT .format (((DateTime ) value ).getJavaInstant ())));
125120 } else if (value instanceof UUID ) {
126- holder . add ( fieldName , new JsonPrimitive (value .toString () ));
121+ return new JsonPrimitive (value .toString ());
127122 } else if (value instanceof ByteString ) {
128- final JsonPrimitive base64value = convertByteString ((ByteString ) value );
129- holder .add (fieldName , base64value );
123+ return convertByteString ((ByteString ) value );
130124 } else if (value instanceof XmlElement ) {
131125 final String fragment = ((XmlElement ) value ).getFragment ();
132126 if (fragment != null ) {
133- holder . add ( fieldName , new JsonPrimitive (fragment ) );
127+ return new JsonPrimitive (fragment );
134128 }
129+ return null ;
135130 } else if (value instanceof NodeId ) {
136- final JsonObject nodeIdObj = convertNodeId ((NodeId ) value , reversibleMode , fieldName );
137- holder .add (fieldName , nodeIdObj );
131+ return convertNodeId ((NodeId ) value );
138132 } else if (value instanceof ExpandedNodeId ) {
139- holder . add ( fieldName , new JsonPrimitive (((ExpandedNodeId ) value ).toParseableString () ));
133+ return new JsonPrimitive (((ExpandedNodeId ) value ).toParseableString ());
140134 } else if (value instanceof StatusCode ) {
141- holder . add ( fieldName , convertStatusCode ((StatusCode ) value , reversibleMode ) );
135+ return convertStatusCode ((StatusCode ) value );
142136 } else if (value instanceof QualifiedName ) {
143137 final JsonObject qualifiedName = new JsonObject ();
144138 final String name = ((QualifiedName ) value ).getName ();
@@ -149,7 +143,7 @@ private static void convertValue(
149143 if (nsIdx > 0 ) {
150144 qualifiedName .add ("uri" , new JsonPrimitive (nsIdx ));
151145 }
152- holder . add ( fieldName , qualifiedName ) ;
146+ return qualifiedName ;
153147 } else if (value instanceof LocalizedText ) {
154148 final JsonObject localizedText = new JsonObject ();
155149 final String locale = ((LocalizedText ) value ).getLocale ();
@@ -160,87 +154,48 @@ private static void convertValue(
160154 if (text != null ) {
161155 localizedText .add ("text" , new JsonPrimitive (text ));
162156 }
163- holder . add ( fieldName , localizedText ) ;
157+ return localizedText ;
164158 } else if (value instanceof ExtensionObject ) {
165- if (!reversibleMode ) {
166- try {
167- final Object decodedValue = ((ExtensionObject ) value ).decode (serializationContext );
168- convertValue (decodedValue , holder , reversibleMode , fieldName , serializationContext );
169- } catch (final Throwable t ) {
170- log .debug ("Not able to decode body of OPC UA ExtensionObject, using undecoded body value instead" ,
171- t );
172- convertValue (((ExtensionObject ) value ).getBody (),
173- holder ,
174- reversibleMode ,
175- fieldName ,
176- serializationContext );
177- }
178-
179- } else {
180- final JsonObject extensionObject = new JsonObject ();
181- extensionObject .add ("typeId" ,
182- new JsonPrimitive (((ExtensionObject ) value ).getEncodingId ().toParseableString ()));
183- final ExtensionObject .BodyType bodyType = ((ExtensionObject ) value ).getBodyType ();
184- if (bodyType != null ) {
185- switch (bodyType ) {
186- case ByteString :
187- extensionObject .add ("encoding" , new JsonPrimitive (1 ));
188- case XmlElement :
189- extensionObject .add ("encoding" , new JsonPrimitive (2 ));
190- }
191- }
192-
159+ try {
193160 final Object decodedValue = ((ExtensionObject ) value ).decode (serializationContext );
194- convertValue (decodedValue , extensionObject , reversibleMode , "body" , serializationContext );
195- holder .add (fieldName , extensionObject );
161+ return convertValue (decodedValue , serializationContext );
162+ } catch (final Throwable t ) {
163+ log .debug ("Not able to decode body of OPC UA ExtensionObject, using undecoded body value instead" ,
164+ t );
165+ return convertValue (((ExtensionObject ) value ).getBody (), serializationContext );
196166 }
197167 } else if (value instanceof Variant ) {
198- if (!reversibleMode ) {
199- convertValue (((Variant ) value ).getValue (), holder , reversibleMode , fieldName , serializationContext );
200- } else {
201- final JsonObject variant = new JsonObject ();
202- final Optional <ExpandedNodeId > dataType = ((Variant ) value ).getDataType ();
203- if (dataType .isPresent ()) {
204- final Number typeId = dataType .get ().getNamespaceIndex ();
205- variant .add ("type" , new JsonPrimitive (typeId ));
206- }
207- convertValue (((Variant ) value ).getValue (), variant , reversibleMode , "body" , serializationContext );
208- holder .add (fieldName , variant );
209- }
168+ return convertValue (((Variant ) value ).getValue (), serializationContext );
210169 } else if (value instanceof DiagnosticInfo ) {
211- final JsonObject diagnosticInfo = convertDiagnosticInfo ((DiagnosticInfo ) value , reversibleMode );
212- holder .add (fieldName , diagnosticInfo );
170+ return convertDiagnosticInfo ((DiagnosticInfo ) value );
213171 } else if (value instanceof Struct ) {
214172 final Struct struct = (Struct ) value ;
215173 final JsonObject structRoot = new JsonObject ();
216- for (final Struct .Member member : struct .getMembers ().values ()) {
217- convertValue (member .getValue (), structRoot , reversibleMode , member .getName (), serializationContext );
218- }
219- holder .add (fieldName , structRoot );
174+ struct .getMembers ()
175+ .values ()
176+ .forEach (member -> structRoot .add (member .getName (), convertValue (member .getValue (), serializationContext )));
177+ return structRoot ;
178+ } else if (value .getClass ().isArray ()) {
179+ final Object [] values = (Object [])value ;
180+ final JsonArray ret = new JsonArray ();
181+ Arrays .asList (values ).forEach (in -> ret .add (convertValue (in , serializationContext )));
182+ return ret ;
220183 } else {
221- //fallback, best effort
222- if (log .isTraceEnabled ()) {
223- log .trace ("No explicit converter for OPC UA type " +
224- value .getClass ().getSimpleName () +
225- " falling back to best effort json" );
226- }
227- holder .add (fieldName , GSON .toJsonTree (value ));
184+ log .warn ("No explicit converter for OPC UA type " +
185+ value .getClass ().getSimpleName () +
186+ " falling back to best effort json" );
187+ return GSON .toJsonTree (value );
228188 }
229189 }
230190
231191 @ NotNull
232- private static JsonElement convertStatusCode (final @ NotNull StatusCode value , final boolean reversibleMode ) {
233- if (reversibleMode ) {
234- return new JsonPrimitive (value .getValue ());
235- }
236-
192+ private static JsonElement convertStatusCode (final @ NotNull StatusCode value ) {
237193 final JsonObject statusCode = new JsonObject ();
238194 final long statusCodeNr = value .getValue ();
239195 statusCode .add ("code" , new JsonPrimitive (statusCodeNr ));
240- final Optional <String []> statusNamingOptional = StatusCodes .lookup (statusCodeNr );
241- if (statusNamingOptional .isPresent ()) {
242- statusCode .add ("symbol" , new JsonPrimitive (statusNamingOptional .get ()[0 ]));
243- }
196+ StatusCodes
197+ .lookup (statusCodeNr )
198+ .ifPresent (code -> statusCode .add ("symbol" , new JsonPrimitive (code [0 ])));
244199 return statusCode ;
245200 }
246201
@@ -252,7 +207,7 @@ private static JsonPrimitive convertByteString(final @NotNull ByteString value)
252207
253208 @ NotNull
254209 private static JsonObject convertNodeId (
255- final @ NotNull NodeId nodeId , final boolean reversibleMode , @ NotNull final String fieldName ) {
210+ final @ NotNull NodeId nodeId ) {
256211 final JsonObject nodeIdObj = new JsonObject ();
257212
258213 switch (nodeId .getType ()) {
@@ -274,61 +229,15 @@ private static JsonObject convertNodeId(
274229 }
275230
276231 final int namespaceIndex = nodeId .getNamespaceIndex ().intValue ();
277- if (reversibleMode ) {
278- if (namespaceIndex != 0 ) {
279- nodeIdObj .add ("namespace" , new JsonPrimitive (namespaceIndex ));
280- }
232+ if (namespaceIndex == 1 ) { // 1 is always encoded as a number
233+ nodeIdObj .add ("namespace" , new JsonPrimitive (namespaceIndex ));
281234 } else {
282- if (namespaceIndex == 1 ) { // 1 is always encoded as a number
283- nodeIdObj .add ("namespace" , new JsonPrimitive (namespaceIndex ));
284- } else {
285- nodeIdObj .add ("namespace" , new JsonPrimitive (nodeId .toParseableString ()));
286- }
235+ nodeIdObj .add ("namespace" , new JsonPrimitive (nodeId .toParseableString ()));
287236 }
288237 return nodeIdObj ;
289238 }
290239
291- @ NotNull
292- private static JsonObject convertExpandedNodeId (
293- final @ NotNull ExpandedNodeId nodeId , final boolean reversibleMode , @ NotNull final String fieldName ) {
294- final JsonObject nodeIdObj = new JsonObject ();
295-
296- switch (nodeId .getType ()) {
297- case Numeric :
298- nodeIdObj .add ("id" , new JsonPrimitive ((Number ) nodeId .getIdentifier ()));
299- break ;
300- case String :
301- nodeIdObj .add ("idType" , new JsonPrimitive (1 ));
302- nodeIdObj .add ("id" , new JsonPrimitive ((String ) nodeId .getIdentifier ()));
303- break ;
304- case Guid :
305- nodeIdObj .add ("idType" , new JsonPrimitive (2 ));
306- nodeIdObj .add ("id" , new JsonPrimitive (nodeId .getIdentifier ().toString ())); //UUID.toString()
307- break ;
308- case Opaque : //ByteString
309- nodeIdObj .add ("idType" , new JsonPrimitive (3 ));
310- nodeIdObj .add ("id" , convertByteString ((ByteString ) nodeId .getIdentifier ())); //UUID.toString()
311- break ;
312- }
313-
314- final int namespaceIndex = nodeId .getNamespaceIndex ().intValue ();
315- if (reversibleMode ) {
316- if (namespaceIndex != 0 ) {
317- nodeIdObj .add ("namespace" , new JsonPrimitive (namespaceIndex ));
318- }
319- } else {
320- if (namespaceIndex == 1 ) { // 1 is always encoded as a number
321- nodeIdObj .add ("namespace" , new JsonPrimitive (namespaceIndex ));
322- } else {
323- nodeIdObj .add ("namespace" , new JsonPrimitive (nodeId .toParseableString ()));
324- }
325- }
326-
327- nodeIdObj .add ("serverUri" , new JsonPrimitive (nodeId .getServerIndex ().longValue ()));
328- return nodeIdObj ;
329- }
330-
331- private static @ NotNull JsonObject convertDiagnosticInfo (final DiagnosticInfo value , final boolean reversibleMode ) {
240+ private static @ NotNull JsonObject convertDiagnosticInfo (final DiagnosticInfo value ) {
332241 final JsonObject diagnosticInfo = new JsonObject ();
333242 diagnosticInfo .add ("symbolicId" , new JsonPrimitive (value .getSymbolicId ()));
334243 diagnosticInfo .add ("namespaceUri" , new JsonPrimitive (value .getNamespaceUri ()));
@@ -338,17 +247,17 @@ private static JsonObject convertExpandedNodeId(
338247 diagnosticInfo .add ("additionalInfo" , new JsonPrimitive (value .getAdditionalInfo ()));
339248 }
340249 if (value .getInnerStatusCode () != null ) {
341- diagnosticInfo .add ("innerStatusCode" , convertStatusCode (value .getInnerStatusCode (), reversibleMode ));
250+ diagnosticInfo .add ("innerStatusCode" , convertStatusCode (value .getInnerStatusCode ()));
342251 }
343252 if (value .getInnerDiagnosticInfo () != null ) {
344253 diagnosticInfo .add ("innerDiagnosticInfo" ,
345- convertDiagnosticInfo (value .getInnerDiagnosticInfo (), reversibleMode ));
254+ convertDiagnosticInfo (value .getInnerDiagnosticInfo ()));
346255 }
347256 return diagnosticInfo ;
348257 }
349258
350259 private static void addDataValueFields (
351- final @ NotNull DataValue dataValue , final @ NotNull JsonObject jsonObject , final boolean reversibleMode ) {
260+ final @ NotNull DataValue dataValue , final @ NotNull JsonObject jsonObject ) {
352261 if (dataValue .getServerTime () != null ) {
353262 final Instant javaInstant = dataValue .getServerTime ().getJavaInstant ();
354263 jsonObject .add ("serverTimestamp" , new JsonPrimitive (DateTimeFormatter .ISO_INSTANT .format (javaInstant )));
@@ -364,7 +273,7 @@ private static void addDataValueFields(
364273 jsonObject .add ("sourcePicoSeconds" , new JsonPrimitive (dataValue .getServerPicoseconds ().intValue ()));
365274 }
366275 if (dataValue .getStatusCode () != null && dataValue .getStatusCode ().getValue () > 0 ) {
367- jsonObject .add ("status" , convertStatusCode (dataValue .getStatusCode (), reversibleMode ));
276+ jsonObject .add ("status" , convertStatusCode (dataValue .getStatusCode ()));
368277 }
369278 }
370279}
0 commit comments