Reputation: 21
I want to populate a struct to write to an opcua server via Apache Milo. The milo client does not have any prior information on the custom data types and everything must be discovered at runtime. The information on the struct (field names and data types) is used to consume a json, populate a struct with it and write to the server.
I added a SessionInitializer to retrieve the data type definitions at connection establishment, which works:
opcUaClient.addSessionInitializer(new DataTypeDictionarySessionInitializer(new GenericBsdParser()));
However using them to populate the struct was a struggle and seems very hacky. I get the Codec for the data type, however the internals are hidden as a private field, which I access via Reflection, which likely will break in later Java Versions (We are currently at 11):
final GenericStructCodec dataTypeCodec =
(GenericStructCodec) client.getDynamicSerializationContext().getDataTypeManager().getCodec(dataTypeNodeId);
Field f = AbstractCodec.class.getDeclaredField("fields");
f.setAccessible(true);
Map<String, FieldType> fields = (Map<String, FieldType>) f.get(dataTypeCodec);
Using this information I parse the Json (which is already parsed to a Map in my code)
final Struct.Builder builder = Struct.builder("ScanSettings");
for (final Map.Entry<String, FieldType> entry : fields.entrySet()) {
String key = entry.getKey();
final FieldType fieldType = entry.getValue();
final Object jsonNode = rootNode.get(key);
switch (fieldType.getTypeName().toString()) {
case ("{http://opcfoundation.org/BinarySchema/}UInt32"):
builder.addMember(key, UInteger.valueOf(jsonNode.toString()));
break;
case ("{http://opcfoundation.org/BinarySchema/}String"):
builder.addMember(key, jsonNode);
break;
case ("{http://opcfoundation.org/BinarySchema/}Boolean"):
builder.addMember(key, Boolean.parseBoolean(jsonNode.toString()));
break;
case ("{urn:test:testns}CustomEnumType"):
final @Nullable DataTypeCodec enumTypeCodec =
client.getDynamicSerializationContext()
.getDataTypeManager()
.getCodec(dataTypeNodeId);
System.err.println(dataTypeCodec);
builder.addMember(key, Integer.valueOf(jsonNode.toString()));
break;
}
}
return ExtensionObject.encode(client.getDynamicSerializationContext(),
builder.build(),
dataTypeNodeId,
OpcUaDefaultBinaryEncoding.getInstance());
The switch on the TypeName looks also problematic as the strings may be different I guess depending on the server implementation. Of course it misses many types and needs recursion for embedded struct, but I'm really not confident with my general approach. Is there a better way to get information on the custom data types and use them to populate a struct?
Upvotes: 2
Views: 99