From 0537ceb7c70cc0b8159cb2a8d3004502678f6222 Mon Sep 17 00:00:00 2001 From: Timm Fitschen <t.fitschen@indiscale.com> Date: Thu, 13 Feb 2020 23:41:00 +0100 Subject: [PATCH] BUG: handling of empty text values and null values --- .../caosdb/datetime/SemiCompleteDateTime.java | 6 - .../java/caosdb/datetime/UTCDateTime.java | 6 - .../transaction/InsertEntityProperties.java | 25 +- .../server/datatype/AbstractEnumValue.java | 6 - .../server/datatype/CollectionValue.java | 31 +- .../caosdb/server/datatype/GenericValue.java | 13 +- .../server/datatype/IndexedSingleValue.java | 8 - .../caosdb/server/datatype/ListDatatype.java | 9 +- .../server/datatype/ReferenceValue.java | 6 - .../caosdb/server/datatype/SingleValue.java | 4 - .../server/datatype/TimespanDatatype.java | 291 +++++++++--------- .../java/caosdb/server/entity/Entity.java | 17 +- .../entity/xml/PropertyToElementStrategy.java | 3 +- 13 files changed, 206 insertions(+), 219 deletions(-) diff --git a/src/main/java/caosdb/datetime/SemiCompleteDateTime.java b/src/main/java/caosdb/datetime/SemiCompleteDateTime.java index 9082e5bf..55a7f5d5 100644 --- a/src/main/java/caosdb/datetime/SemiCompleteDateTime.java +++ b/src/main/java/caosdb/datetime/SemiCompleteDateTime.java @@ -24,7 +24,6 @@ package caosdb.datetime; import caosdb.server.datatype.AbstractDatatype.Table; import java.util.TimeZone; -import org.jdom2.Attribute; import org.jdom2.Element; public class SemiCompleteDateTime extends FragmentDateTime implements Interval { @@ -47,11 +46,6 @@ public class SemiCompleteDateTime extends FragmentDateTime implements Interval { throw new UnsupportedOperationException("This DateTime cannot be stored to the database."); } - @Override - public void addToAttribute(final Attribute a) { - throw new UnsupportedOperationException("This DateTime cannot be added to an xml attribute."); - } - @Override public String toDateTimeString(final TimeZone tz) { final StringBuilder sb = new StringBuilder(); diff --git a/src/main/java/caosdb/datetime/UTCDateTime.java b/src/main/java/caosdb/datetime/UTCDateTime.java index 72a55faa..215de67b 100644 --- a/src/main/java/caosdb/datetime/UTCDateTime.java +++ b/src/main/java/caosdb/datetime/UTCDateTime.java @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.TimeZone; -import org.jdom2.Attribute; import org.jdom2.Element; public class UTCDateTime implements Interval { @@ -292,11 +291,6 @@ public class UTCDateTime implements Interval { return sb.toString(); } - @Override - public void addToAttribute(final Attribute a) { - a.setValue(toDateTimeString(TimeZone.getDefault())); - } - @Override public String toString() { throw new NullPointerException("toString method!!!"); diff --git a/src/main/java/caosdb/server/database/backend/transaction/InsertEntityProperties.java b/src/main/java/caosdb/server/database/backend/transaction/InsertEntityProperties.java index 05e4dbce..a777d420 100644 --- a/src/main/java/caosdb/server/database/backend/transaction/InsertEntityProperties.java +++ b/src/main/java/caosdb/server/database/backend/transaction/InsertEntityProperties.java @@ -30,6 +30,7 @@ import caosdb.server.database.proto.FlatProperty; import caosdb.server.datatype.AbstractCollectionDatatype; import caosdb.server.datatype.AbstractDatatype.Table; import caosdb.server.datatype.CollectionValue; +import caosdb.server.datatype.IndexedSingleValue; import caosdb.server.datatype.SingleValue; import caosdb.server.entity.EntityInterface; import caosdb.server.entity.Role; @@ -37,6 +38,7 @@ import caosdb.server.entity.StatementStatus; import caosdb.server.entity.wrapper.Property; import java.util.ArrayList; import java.util.Deque; +import java.util.Iterator; import java.util.List; public class InsertEntityProperties extends BackendTransaction { @@ -111,22 +113,33 @@ public class InsertEntityProperties extends BackendTransaction { if (property.getValue() instanceof CollectionValue) { // insert collection of values final CollectionValue v = (CollectionValue) property.getValue(); + Iterator<IndexedSingleValue> iterator = v.iterator(); + final SingleValue firstValue = iterator.next(); // insert 2nd to nth item for (int i = 1; i < v.size(); i++) { - final SingleValue vi = v.get(i); - table = (vi != null ? vi.getTable() : Table.null_data); + final SingleValue vi = iterator.next(); fp.idx = i; - fp.value = (vi != null ? vi.toDatabaseString() : null); + if (vi == null) { + fp.value = null; + table = Table.null_data; + } else { + fp.value = vi.toDatabaseString(); + table = vi.getTable(); + } t.execute( domain, (entity != null ? entity : property.getDomain()), fp, table, unit_sig); } // insert first item - final SingleValue vi = v.get(0); fp.idx = 0; - fp.value = vi.toDatabaseString(); - table = (vi != null ? vi.getTable() : Table.null_data); + if (firstValue == null) { + fp.value = null; + table = Table.null_data; + } else { + fp.value = firstValue.toDatabaseString(); + table = firstValue.getTable(); + } } else { // insert single value diff --git a/src/main/java/caosdb/server/datatype/AbstractEnumValue.java b/src/main/java/caosdb/server/datatype/AbstractEnumValue.java index 9dd3a687..33303f22 100644 --- a/src/main/java/caosdb/server/datatype/AbstractEnumValue.java +++ b/src/main/java/caosdb/server/datatype/AbstractEnumValue.java @@ -24,7 +24,6 @@ package caosdb.server.datatype; import caosdb.server.datatype.AbstractDatatype.Table; import com.google.common.base.Objects; -import org.jdom2.Attribute; import org.jdom2.Element; public abstract class AbstractEnumValue implements SingleValue { @@ -50,11 +49,6 @@ public abstract class AbstractEnumValue implements SingleValue { return this.value.toString(); } - @Override - public final void addToAttribute(final Attribute a) { - a.setValue(this.value.toString()); - } - @Override public boolean equals(final Object obj) { if (obj instanceof AbstractEnumValue) { diff --git a/src/main/java/caosdb/server/datatype/CollectionValue.java b/src/main/java/caosdb/server/datatype/CollectionValue.java index 65c8fa5f..3e70de73 100644 --- a/src/main/java/caosdb/server/datatype/CollectionValue.java +++ b/src/main/java/caosdb/server/datatype/CollectionValue.java @@ -22,30 +22,30 @@ */ package caosdb.server.datatype; -import java.util.LinkedList; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; import org.jdom2.Element; -public class CollectionValue extends LinkedList<IndexedSingleValue> implements Value { +public class CollectionValue implements Value, Iterable<IndexedSingleValue> { - private static final long serialVersionUID = 1L; + private List<IndexedSingleValue> list = new ArrayList<>(); @Override public void addToElement(final Element e) { for (final SingleValue v : this) { final Element valueElem = new Element("Value"); - if (v != null) { - v.addToElement(valueElem); - } + v.addToElement(valueElem); e.addContent(valueElem); } } - @Override public boolean add(final IndexedSingleValue e) { if (e == null) { - return super.add(new IndexedSingleValue(null)); + return list.add(new IndexedSingleValue(null)); } - return super.add(e); + return list.add(e); } public void add(final SingleValue v) { @@ -67,4 +67,17 @@ public class CollectionValue extends LinkedList<IndexedSingleValue> implements V throw new IllegalArgumentException("Expected a SingleValue."); } } + + @Override + public Iterator<IndexedSingleValue> iterator() { + return list.iterator(); + } + + public void sort() { + Collections.sort(list); + } + + public int size() { + return list.size(); + } } diff --git a/src/main/java/caosdb/server/datatype/GenericValue.java b/src/main/java/caosdb/server/datatype/GenericValue.java index 8e8f1dd4..cffd62bb 100644 --- a/src/main/java/caosdb/server/datatype/GenericValue.java +++ b/src/main/java/caosdb/server/datatype/GenericValue.java @@ -24,7 +24,6 @@ package caosdb.server.datatype; import caosdb.server.datatype.AbstractDatatype.Table; import com.google.common.base.Objects; -import org.jdom2.Attribute; import org.jdom2.Element; public class GenericValue implements SingleValue { @@ -61,7 +60,12 @@ public class GenericValue implements SingleValue { @Override public void addToElement(final Element e) { - e.addContent(this.value.toString()); + if (this.value instanceof String && ((String) this.value).isEmpty()) { + Element empty = new Element("EmptyString"); + e.addContent(empty); + } else { + e.addContent(this.value.toString()); + } } @Override @@ -74,11 +78,6 @@ public class GenericValue implements SingleValue { return this.value.toString(); } - @Override - public void addToAttribute(final Attribute a) { - a.setValue(this.value.toString()); - } - @Override public String toString() { return toDatabaseString(); diff --git a/src/main/java/caosdb/server/datatype/IndexedSingleValue.java b/src/main/java/caosdb/server/datatype/IndexedSingleValue.java index 01858228..f33db43e 100644 --- a/src/main/java/caosdb/server/datatype/IndexedSingleValue.java +++ b/src/main/java/caosdb/server/datatype/IndexedSingleValue.java @@ -23,7 +23,6 @@ package caosdb.server.datatype; import caosdb.server.datatype.AbstractDatatype.Table; -import org.jdom2.Attribute; import org.jdom2.Element; public class IndexedSingleValue implements SingleValue, Comparable<IndexedSingleValue> { @@ -63,13 +62,6 @@ public class IndexedSingleValue implements SingleValue, Comparable<IndexedSingle return this.wrapped.toDatabaseString(); } - @Override - public void addToAttribute(final Attribute a) { - if (this.wrapped != null) { - this.wrapped.addToAttribute(a); - } - } - public void setIndex(final int i) { this.index = i; } diff --git a/src/main/java/caosdb/server/datatype/ListDatatype.java b/src/main/java/caosdb/server/datatype/ListDatatype.java index e61a1a3b..87790145 100644 --- a/src/main/java/caosdb/server/datatype/ListDatatype.java +++ b/src/main/java/caosdb/server/datatype/ListDatatype.java @@ -23,7 +23,6 @@ package caosdb.server.datatype; import caosdb.server.entity.Message; -import java.util.Collections; public class ListDatatype extends AbstractCollectionDatatype { @@ -38,9 +37,13 @@ public class ListDatatype extends AbstractCollectionDatatype { final CollectionValue ret = new CollectionValue(); if (value instanceof CollectionValue) { final CollectionValue colValue = (CollectionValue) value; - Collections.sort(colValue); + colValue.sort(); for (final IndexedSingleValue singleValue : colValue) { - ret.add(singleValue.getIndex(), getDatatype().parseValue(singleValue.getWrapped())); + if (singleValue.getWrapped() == null) { + ret.add(singleValue.getIndex(), null); + } else { + ret.add(singleValue.getIndex(), getDatatype().parseValue(singleValue.getWrapped())); + } } } else { ret.add(getDatatype().parseValue(value)); diff --git a/src/main/java/caosdb/server/datatype/ReferenceValue.java b/src/main/java/caosdb/server/datatype/ReferenceValue.java index 17fea765..25fa0b38 100644 --- a/src/main/java/caosdb/server/datatype/ReferenceValue.java +++ b/src/main/java/caosdb/server/datatype/ReferenceValue.java @@ -26,7 +26,6 @@ import caosdb.server.datatype.AbstractDatatype.Table; import caosdb.server.entity.EntityInterface; import caosdb.server.entity.Message; import caosdb.server.utils.ServerMessages; -import org.jdom2.Attribute; import org.jdom2.Element; public class ReferenceValue implements SingleValue { @@ -122,11 +121,6 @@ public class ReferenceValue implements SingleValue { return toString(); } - @Override - public void addToAttribute(final Attribute a) { - a.setValue(toString()); - } - @Override public boolean equals(final Object obj) { if (obj instanceof ReferenceValue) { diff --git a/src/main/java/caosdb/server/datatype/SingleValue.java b/src/main/java/caosdb/server/datatype/SingleValue.java index 60f370ed..2ae17a61 100644 --- a/src/main/java/caosdb/server/datatype/SingleValue.java +++ b/src/main/java/caosdb/server/datatype/SingleValue.java @@ -22,13 +22,9 @@ */ package caosdb.server.datatype; -import org.jdom2.Attribute; - public interface SingleValue extends Value { public AbstractDatatype.Table getTable(); public String toDatabaseString(); - - public void addToAttribute(Attribute a); } diff --git a/src/main/java/caosdb/server/datatype/TimespanDatatype.java b/src/main/java/caosdb/server/datatype/TimespanDatatype.java index bdf83800..ef27eb67 100644 --- a/src/main/java/caosdb/server/datatype/TimespanDatatype.java +++ b/src/main/java/caosdb/server/datatype/TimespanDatatype.java @@ -1,148 +1,143 @@ -/* - * ** header v3.0 - * This file is a part of the CaosDB Project. - * - * Copyright (C) 2018 Research Group Biomedical Physics, - * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - * - * ** end header - */ -package caosdb.server.datatype; - -import caosdb.server.datatype.AbstractDatatype.Table; -import caosdb.server.entity.Message; -import org.jdom2.Attribute; -import org.jdom2.Element; - -@DatatypeDefinition(name = "Timespan") -public class TimespanDatatype extends AbstractDatatype { - - public static final Message TIMESPAN_NOT_PARSABLE = new Message(219, "Timespan not parsable."); - - @Override - public Timespan parseValue(final Object value) throws Message { - Timespan ret = null; - try { - ret = Timespan.parse(value.toString()); - } catch (final NumberFormatException e) { - // TODO define correct error message - throw TIMESPAN_NOT_PARSABLE; - } - return ret; - } -} - -class Timespan implements SingleValue { - public enum Unit { - YEARS, - MONTHS, - WEEKS, - DAYS, - HOURS, - MINUTES, - SECONDS, - USERDEFINED - }; - - static Timespan parse(final String str) throws NumberFormatException { - - final Timespan ts = new Timespan(); - String[] parts; - if (str.matches("^\\s*[0-9]+(\\.[0-9]+)?([eE]-?[0-9]+)?\\s*[A-Za-z]+\\s*$")) { - parts = new String[2]; - parts[0] = str.split("\\s*?[A-Za-z]*$")[0]; - parts[1] = str.split("^[0-9]+(\\.[0-9]+)?([eE]-?[0-9]+)?\\s*")[1]; - - if (parts[1].equalsIgnoreCase(Unit.YEARS.toString()) || parts[1].equalsIgnoreCase("y")) { - ts.setUnit(Unit.YEARS); - ts.factor = 31556925.261; - } else if (parts[1].equalsIgnoreCase(Unit.MONTHS.toString()) || parts[1].equals("M")) { - ts.setUnit(Unit.MONTHS); - ts.factor = 31556925.261 / 12; - } else if (parts[1].equalsIgnoreCase(Unit.WEEKS.toString()) - || parts[1].equalsIgnoreCase("w")) { - ts.setUnit(Unit.WEEKS); - ts.factor = 7 * 86400.0; - } else if (parts[1].equalsIgnoreCase(Unit.DAYS.toString()) - || parts[1].equalsIgnoreCase("d")) { - ts.setUnit(Unit.DAYS); - ts.factor = 86400.0; - } else if (parts[1].equalsIgnoreCase(Unit.HOURS.toString()) - || parts[1].equalsIgnoreCase("h")) { - ts.setUnit(Unit.HOURS); - ts.factor = 3600.0; - } else if (parts[1].equalsIgnoreCase(Unit.MINUTES.toString()) || parts[1].equals("m")) { - ts.setUnit(Unit.MINUTES); - ts.factor = 60.0; - } else if (parts[1].equalsIgnoreCase(Unit.SECONDS.toString()) - || parts[1].equalsIgnoreCase("s")) { - ts.setUnit(Unit.SECONDS); - } else { - ts.setUnit(Unit.USERDEFINED); - } - ts.value = Double.parseDouble(parts[0]); - } else if (str.matches("^\\s*[0-9]+(\\.[0-9]+)?([eE]-?[0-9]+)?\\s*$")) { - ts.setUnit(null); - ts.value = Double.parseDouble(str); - - } else { - throw new NumberFormatException("Couldn't parse String to Timespan."); - } - return ts; - } - - private double value = 0; - private double factor = 1; - - private Unit unit = null; - - public double toSeconds() { - return value * factor; - } - - @Override - public String toString() { - return Double.toString(value); - } - - public String getUnit() { - return unit.toString(); - } - - private void setUnit(final Unit unit) { - this.unit = unit; - } - - @Override - public void addToElement(Element e) { - e.addContent(toString()); - } - - @Override - public Table getTable() { - return Table.double_data; - } - - @Override - public String toDatabaseString() { - return toString(); - } - - @Override - public void addToAttribute(Attribute a) { - a.setValue(toString()); - } -} +/// * +// * ** header v3.0 +// * This file is a part of the CaosDB Project. +// * +// * Copyright (C) 2018 Research Group Biomedical Physics, +// * Max-Planck-Institute for Dynamics and Self-Organization Göttingen +// * +// * This program is free software: you can redistribute it and/or modify +// * it under the terms of the GNU Affero General Public License as +// * published by the Free Software Foundation, either version 3 of the +// * License, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU Affero General Public License for more details. +// * +// * You should have received a copy of the GNU Affero General Public License +// * along with this program. If not, see <https://www.gnu.org/licenses/>. +// * +// * ** end header +// */ +// package caosdb.server.datatype; +// +// import org.jdom2.Element; +// import caosdb.server.datatype.AbstractDatatype.Table; +// import caosdb.server.entity.Message; +// +// @DatatypeDefinition(name = "Timespan") +// public class TimespanDatatype extends AbstractDatatype { +// +// public static final Message TIMESPAN_NOT_PARSABLE = new Message(219, "Timespan not parsable."); +// +// @Override +// public Timespan parseValue(final Object value) throws Message { +// Timespan ret = null; +// try { +// ret = Timespan.parse(value.toString()); +// } catch (final NumberFormatException e) { +// // TODO define correct error message +// throw TIMESPAN_NOT_PARSABLE; +// } +// return ret; +// } +// } +// +// class Timespan implements SingleValue { +// public enum Unit { +// YEARS, +// MONTHS, +// WEEKS, +// DAYS, +// HOURS, +// MINUTES, +// SECONDS, +// USERDEFINED +// }; +// +// static Timespan parse(final String str) throws NumberFormatException { +// +// final Timespan ts = new Timespan(); +// String[] parts; +// if (str.matches("^\\s*[0-9]+(\\.[0-9]+)?([eE]-?[0-9]+)?\\s*[A-Za-z]+\\s*$")) { +// parts = new String[2]; +// parts[0] = str.split("\\s*?[A-Za-z]*$")[0]; +// parts[1] = str.split("^[0-9]+(\\.[0-9]+)?([eE]-?[0-9]+)?\\s*")[1]; +// +// if (parts[1].equalsIgnoreCase(Unit.YEARS.toString()) || parts[1].equalsIgnoreCase("y")) { +// ts.setUnit(Unit.YEARS); +// ts.factor = 31556925.261; +// } else if (parts[1].equalsIgnoreCase(Unit.MONTHS.toString()) || parts[1].equals("M")) { +// ts.setUnit(Unit.MONTHS); +// ts.factor = 31556925.261 / 12; +// } else if (parts[1].equalsIgnoreCase(Unit.WEEKS.toString()) +// || parts[1].equalsIgnoreCase("w")) { +// ts.setUnit(Unit.WEEKS); +// ts.factor = 7 * 86400.0; +// } else if (parts[1].equalsIgnoreCase(Unit.DAYS.toString()) +// || parts[1].equalsIgnoreCase("d")) { +// ts.setUnit(Unit.DAYS); +// ts.factor = 86400.0; +// } else if (parts[1].equalsIgnoreCase(Unit.HOURS.toString()) +// || parts[1].equalsIgnoreCase("h")) { +// ts.setUnit(Unit.HOURS); +// ts.factor = 3600.0; +// } else if (parts[1].equalsIgnoreCase(Unit.MINUTES.toString()) || parts[1].equals("m")) { +// ts.setUnit(Unit.MINUTES); +// ts.factor = 60.0; +// } else if (parts[1].equalsIgnoreCase(Unit.SECONDS.toString()) +// || parts[1].equalsIgnoreCase("s")) { +// ts.setUnit(Unit.SECONDS); +// } else { +// ts.setUnit(Unit.USERDEFINED); +// } +// ts.value = Double.parseDouble(parts[0]); +// } else if (str.matches("^\\s*[0-9]+(\\.[0-9]+)?([eE]-?[0-9]+)?\\s*$")) { +// ts.setUnit(null); +// ts.value = Double.parseDouble(str); +// +// } else { +// throw new NumberFormatException("Couldn't parse String to Timespan."); +// } +// return ts; +// } +// +// private double value = 0; +// private double factor = 1; +// +// private Unit unit = null; +// +// public double toSeconds() { +// return value * factor; +// } +// +// @Override +// public String toString() { +// return Double.toString(value); +// } +// +// public String getUnit() { +// return unit.toString(); +// } +// +// private void setUnit(final Unit unit) { +// this.unit = unit; +// } +// +// @Override +// public void addToElement(Element e) { +// e.addContent(toString()); +// } +// +// @Override +// public Table getTable() { +// return Table.double_data; +// } +// +// @Override +// public String toDatabaseString() { +// return toString(); +// } +// +// } diff --git a/src/main/java/caosdb/server/entity/Entity.java b/src/main/java/caosdb/server/entity/Entity.java index f9713af2..5c997d7f 100644 --- a/src/main/java/caosdb/server/entity/Entity.java +++ b/src/main/java/caosdb/server/entity/Entity.java @@ -794,15 +794,18 @@ public class Entity extends AbstractObservable implements EntityInterface { final CollectionValue vals = new CollectionValue(); int pidx = 0; for (final Element pe : element.getChildren()) { - if (pe.getName().equalsIgnoreCase("Value")) { + if (pe.getName().equalsIgnoreCase("EmptyString")) { + // special case: empty string which cannot be distinguished from null + // values otherwise. + setValue(new GenericValue("")); + } else if (pe.getName().equalsIgnoreCase("Value")) { // Parse sub-elements which represent VALUES of this entity. - - if (pe.getText() != null && pe.getTextTrim() != "") { + if (pe.getChild("EmptyString") != null) { + vals.add(new GenericValue("")); + } else if (pe.getText() != null && pe.getTextTrim() != "") { vals.add(new GenericValue(pe.getTextTrim())); - } else if (pe.getAttribute("null") != null) { - vals.add(null); } else { - vals.add(new GenericValue("")); + vals.add(null); } } else if (pe.getName().equalsIgnoreCase("Property")) { // Parse sub elements which represent PROPERTIES of this @@ -866,8 +869,6 @@ public class Entity extends AbstractObservable implements EntityInterface { // Parse VALUE. if (vals.size() != 0) { setValue(vals); - } else if (element.getAttribute("null") != null) { - setValue(null); } else if (element.getTextTrim() != null && !element.getTextTrim().equals("")) { setValue(new GenericValue(element.getTextTrim())); } diff --git a/src/main/java/caosdb/server/entity/xml/PropertyToElementStrategy.java b/src/main/java/caosdb/server/entity/xml/PropertyToElementStrategy.java index 5fe0c0c5..a88b47cb 100644 --- a/src/main/java/caosdb/server/entity/xml/PropertyToElementStrategy.java +++ b/src/main/java/caosdb/server/entity/xml/PropertyToElementStrategy.java @@ -47,9 +47,8 @@ public class PropertyToElementStrategy extends EntityToElementStrategy { switch (MagicTypes.getType(entity.getId())) { case UNIT: if (setFieldStrategy.isToBeSet("unit") && v != null) { - final Attribute a = new Attribute("unit", ""); + final Attribute a = new Attribute("unit", ((SingleValue) v).toDatabaseString()); element.setAttribute(a); - ((SingleValue) v).addToAttribute(a); } return element; default: -- GitLab