enhancement
major
minor
major
minor
minor
#29558
XMLInstanceImporter silently imports an internationalized attribute given as value="…" as empty, even when the attribute is mandatory
Affects 8.0.0-SNAPSHOT (2026-09-11), tl-element (XMLInstanceImporter, I18NAttributeStorageMapping).
Initial data (WEB-INF/data/*.objects.xml, InitialDataSetupService) for a type with a mandatory tl.model.i18n:I18NString attribute written the obvious way
{{{#!xml <object type="expenses:ExpenseCategory">
<attribute name="name" value="Travel"/>
</object> }}}
imports the object with an **empty** name and no warning: XMLInstanceImporter.parse hands the value string to the type's storage mapping, and I18NAttributeStorageMapping.getBusinessObject answers null for anything that is not a ResKey. The mandatory constraint is not checked on import either, so the defect surfaces only in the UI (a category without a label).
The exporter's own format works:
{{{#!xml <attribute name="name">
<complex-value><contents><value><en>Travel</en><de>Reise</de></value></contents></complex-value>
</attribute> }}}
Expected: either the importer turns a plain value into a literal ResKey for I18N attributes (the least surprising), or it fails the import with a message naming the attribute and the accepted syntax. Silently importing an empty value for a mandatory attribute is the worst of the three.
Analyse
Der Importer interpretiert ein value="…" als serialisierte Datenbankform des Werts: Der String wird gemäß DB-Typ des Datentyps geparst und durch StorageMapping.getBusinessObject geschickt. Der Datentyp tl.model.i18n:I18NString hat aber keine echte Spaltenabbildung: Seine Werte werden von I18NAttributeStorage in einer eigenen Assoziationstabelle gespeichert, und I18NAttributeStorageMapping ist nur eine Identität für ResKey-Werte, die für jeden anderen Speicherwert stillschweigend null liefert. Der <complex-value>-Pfad funktioniert, weil er die am Anwendungstyp ResKey deklarierte Konfigurations-@Binding verwendet; der value-Pfad ignoriert dagegen das ebenfalls am Anwendungstyp deklarierte Konfigurationsformat (@Format(ResKey.ValueFormat)) vollständig.
Lösung
Ein als value="…" angegebener Wert wird vom Importer zuerst über das Konfigurationsformat (@Format) des Anwendungstyps des Attribut-Datentyps gelesen, sofern der Anwendungstyp eines deklariert – analog zum <complex-value>-Pfad, der die @Binding des Anwendungstyps nutzt. Datentypen ohne solches Format werden unverändert über DB-Typ und Storage-Mapping gelesen.
Für internationalisierte Attribute gilt damit dieselbe Syntax wie für ResKey-Werte überall in der TopLogic-Konfiguration:
{{{#!xml <attribute name="name" value="my.resource.key"/> <attribute name="name" value="#("Travel"@en, "Reise"@de)"/> }}}
Ein nackter String wie value="Travel" bezeichnet einen Ressourcenschlüssel, kein Literal. Ein nicht parsbarer Wert wird als Importfehler mit Attribut und Position protokolliert.
I18NAttributeStorageMapping.getBusinessObject wirft für einen Speicherwert, der kein ResKey ist, eine Exception statt null zu liefern, sodass ein falscher Wert nie mehr stillschweigend als leer importiert wird.
Nicht Teil dieses Tickets: eine Prüfung von Pflichtattributen beim Import.