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:
- it does not work for the DataTypeDefXsd- Enumeration where the serialization value should be "xs:anyURI, xs:int etc."
- 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
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
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:
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:
and by adding a new template
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)!
enum-default-properties.rq
add the member variable "value".
process-enum-individuals.rq
use the constructor and add the value, here the template providing the enum's value is called!
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.
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
AASEnumGeneration.zip