Skip to content

Simplify ENUM usage #5

@dglachs

Description

@dglachs

Hello all,

this is a proposal for improvement ...

currently, the generation of enums simply transforms the name of the class name to a SCREAMING_SNAKE_CASE notation. During de/serialization an extra Serializer/Deserializer has to take care of again transforming the SCREAMING_SNAKE_CASE into the serialization value (CamelCase), e.g.

ASSET_ADMINISTRATION_SHELL --> AssetAdministrationShell
SUBMODEL --> Submodel
SUBMODEL_ELEMENT --> SubmodelElement

this works well, when the transformation is always according to this transformation rule. However, this approach has several drawbacks:

  1. it does not work for the DataTypeDefXsd- Enumeration where the serialization value should be "xs:anyURI, xs:int etc."
  2. the de/serialization module wants to transform every enum, hence the configuration

module.addSerializer(Enum.class, new EnumSerializer());
cannot be used with a ObjectMapper for example with SpringBoot.

To simplify the use of enums with the AAS Model, i'd propose to keep the "serialized" value of the enumeraton such as

ASSET_ADMINISTRATION_SHELL("AssetAministrationShell") in KeyTypes or
ANY_URI("xs:anyURI") in DataTypeDefXsd

This can be achieved by changing few templates:

  • enum-default-methods.rq
  • enum-default-properties.rq
  • process-enum-individuals.rq

and by adding a new template

  • to_enum_value.rq

all of the files are attached!

changes to the distinct files are as follows:

enum-default-methods.rq

Simply generate a constructor, override the toString() method and add a static "fromValue(String text)" method. Optionally (?useJackson) add the required JsonAnnotations for @jsonvalue and @JsonCreator (adopt basic-imports.rq)!

	"\n"
	"\n\t" st:call-template(idstt:to_enum_name, ?class) "(String value) {"
	"\n\t\tthis.value = value;"
	"\n\t}"
	"\n"
	"\n"
	if(?useJackson, "\n\t@JsonValue", "")
	"\n\t@Override"
	"\n\tpublic String toString() {"
	"\n\t\treturn value.toString();"
	"\n\t}"
	if(?useJackson, "\n\t@JsonCreator", "")
 	"\n\tpublic static " st:call-template(idstt:to_enum_name, ?class) " fromValue(String text) {"
	"\n\t\tfor ("st:call-template(idstt:to_enum_name, ?class) " b : " st:call-template(idstt:to_enum_name, ?class)".values()) {"
	"\n\t\t\tif (String.valueOf(b.value).equals(text)) {"
	"\n\t\t\t\treturn b;"
	"\n\t\t\t}"
	"\n\t\t}"
	"\n\t\treturn null;"
  	"\n\t}"

enum-default-properties.rq

add the member variable "value".

	"\n"
	"\n\tprivate String value;"

process-enum-individuals.rq

use the constructor and add the value, here the template providing the enum's value is called!

"\t" ucase(replace(st:call-template(idstt:to_class_name, ?individual), "([a-z])([A-Z])", CONCAT("$1", "_", "$2")))
"(\""
st:call-template(idstt:to_enum_value, ?individual)
"\"),"

to_enum_value.rq

This template checks whether the parent is DataTypeDefXsd and constructs the respective name, taking into account that the definition is named AnyUri (not AnyURI) and the first character is lowercase ...
in all other cases, the name of the class is used.

prefix idstt: <https://w3id.org/idsa/transformationtemplates/>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>
prefix aas: <https://admin-shell.io/aas/3/0/RC02/>
prefix def: <https://admin-shell.io/aas/3/0/RC02/DataTypeDefXsd/>

template idstt:to_enum_value(?class) {
	 
	if ( str(?parent) = str(<aas:DataTypeDefXsd>), 
		if (str(?class) = str(<def:AnyUri>), "xs:anyURI", 
			CONCAT("xs:", LCASE( SUBSTR( ?localName, 1, 1)), SUBSTR( ?localName, 2))
		),
		st:call-template(idstt:to_class_name, ?class)
	)

}
where {
	?class rdf:type ?parent . 
	BIND( st:call-template(idstt:to_class_name, ?class) AS ?localName)
}

Mixin instead of EnumSerializer/EnumDeserializer

using a mixin-class for each of the enums does the job. As a result, the EnumSerializer or Deserializer can be omitted

public abstract class AASEnumMixin<T> {
	@JsonValue
	public abstract String toString();
	@JsonCreator
	public abstract T fromValue(String text);
}

AASEnumGeneration.zip

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type
No fields configured for issues without a type.

Projects

Status
📋 Backlog

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions