# xslt zum Entfernen von unnoetigen Nullkommastellen bei double und float



## Wiplash4 (18. Aug 2021)

Es gibt folgendes Problem. Ich muss xslt verwenden, damit ich unnoetige Nullen von einem double oder float entfernen kann.
Es sollte folgendes erreicht werden.
100 -> 100
100.00 -> 100
100.01 -> 100.01
100.10 -> 100.1
100.11 -> 100.11
100.101 -> 100.101
100.1010 -> 100.101
Wie erreiche ich das?


----------



## Barista (18. Aug 2021)

Du machst es Dir mit Deiner Frage ziemlich einfach.

Stecken die Zahlen in XML?

Wenn ja, in Atributen oder in Elementen?


----------



## kneitzel (18. Aug 2021)

Ich habe es jetzt nicht ausprobiert, aber format-number() kann Zahlen formatieren und existiert schon ab XSLT 1.0:





						XSLT 2.0- und XPath 2.0-Funktionen | format-number
					






					www.data2type.de
				




Wo die Zahlen genau stecken ist ja egal - denn Du selektierst diese ja - so das selektieren klappt, hat man den Wert ja.

Ansonsten wäre die Frage: XSLT 1.0 oder 2.0 erlaubt? Und kann man eine spezifische XML Engine voraussetzen? (Xalan erlaubt z.B. das Einbinden von Typen)

Edit: Falls die '100' immer nur zu '100.' werden sollte: So einen '.' am Ende kann man ja entfernen - z.B. mit substring und string-length
(Ich bin mir nicht sicher, was z.B. ein '####0.#####' erzeugen würde).


----------



## Wiplash4 (18. Aug 2021)

Die Zahlen stammen von einer Java-Klasse und sind dort als Double gespeichert.
[CODE lang="java" title="Java Umwandlung durch xslt"]class Beispiel
{
    Double param1;
    Double param2;
}[/CODE]
. Diese Klasse wird dann in einen String umgewandelt
[CODE lang="java" title="Java Umwandlung Klasse zu String"]final StringWriter sw = new StringWriter();
JAXBContext.newInstance(c).createMarshaller().marshal(o, XMLOutputFactory.newInstance().createXMLStreamWriter(sw));
return (sw.toString());[/CODE]
Sie werden erst in Strings umgewandelt. Sie laufen durch die xslt, wo sie in strings umgewandelt werden.
Sollte es eine Java-Loesung geben, bei der man Standardmaessig beim Umwandeln einer Double in String immer die Trailing decimal places zeros entfernt werden, waere ich offen dafuer.


----------



## kneitzel (18. Aug 2021)

Wiplash4 hat gesagt.:


> Sollte es eine Java-Loesung geben, bei der man Standardmaessig beim Umwandeln einer Double in String immer die Trailing decimal places zeros entfernt werden, waere ich offen dafuer.


Das würde dann sogar noch mehr Sinn machen (Auch wenn es durch ein xslt gehen würde):

[CODE lang="java" title="Mit DecimalFormat"]    public static void formatDouble (double value) {
        System.out.println(new DecimalFormat("#.#####").format(value));
    }
[/CODE]

[CODE lang="java" title="Ohne das DecimalFormat"]    public static void formatDouble (double value) {
        if (value == (long) value) {
            System.out.println("" + (long)value);
        } else {
            System.out.println("" + value);
        }
    }[/CODE]

Edit: Bei dem Format-String aufpassen - wenn da mehr Nachkommastellen sein können, dann muss man da noch # anfügen!


----------



## Wiplash4 (18. Aug 2021)

Danke kneitzel, allerdings wuerde das fuer mich nicht funktionieren. Ist es moeglich das irgendwie ueber das JAXBContext zu machen? Vielleicht mit irgendeinem Flag?


----------



## Wiplash4 (18. Aug 2021)

Barista hat gesagt.:


> Du machst es Dir mit Deiner Frage ziemlich einfach.
> 
> Stecken die Zahlen in XML?
> 
> Wenn ja, in Atributen oder in Elementen?


Oder ja, die Zahlen stecken im XML <tag>1.00</tag> und dann laeuft es durch das xslt.


----------



## kneitzel (18. Aug 2021)

Da würde ich jetzt erst einmal kurz nachhaken: Warum soll es im XML File anders dargestellt werden? Das XML File ist für mich ja erst einmal eine Speicherform der reinen Daten. Daher ist da ein 100.0 auch korrekt. Wieso stört dies? An welcher wird dies für euch zu einem Problem?

Nicht falsch verstehen - aber irgendwie drängt sich der verdacht auf, dass ihr an einer Stelle ein Problem beheben wollt, das eigentlich an einer ganz anderen Stelle besteht.

Und nach #7 (Hat sich etwas überschnitten):
Wenn Ihr da in einem xslt eine Ausgabe baut, dann wäre doch tatsächlich denkbar, da einfach ein format-number vom xslt zu nutzen. Damit bekommt ihr es dann formatiert, so wie ihr es gerne haben wollt.

Edit: Also tatsächlich einfach etwas wie `<xsl:value-of select="format-number($number,'###0.#####')"/>`


----------



## Wiplash4 (18. Aug 2021)

Das xml wird spaeter weiterverwendet und da stoert das .0 einfach. Und dein Verdacht ist nicht falsch, aber dennoch soll es so geloest werden. Die .0 sind einfach unnoetig und sollen so auch nicht abgespeichert werden.
Im JAXB kommt zunaechst kein xslt zum Zuge. Die xslts kommen aber spaeter.


----------



## kneitzel (18. Aug 2021)

Ok, mal ein paar Gedanken zu möglichen Lösungen - aber alles aus dem Kopf heraus geschrieben und daher ggf. Fehlerbehaftet und mehr als Denkanregung!

Eine einfache Möglichkeit wäre ggf:

```
public class MyEntity {
    private double someValue;

    @XmlTransient
    public double getSomeValue() { return someValue; }
    
    @XmlElement(name = "someValue")
    private String getSomeValueFormatted() {
        return new DecimalFormat("#.#####").format(someValue);
    }
}
```
Sprich: Einfach das eigentliche Feld nicht mitnehmen (XmlTransient Annotation) und dann bei einer Methode das XmlElement setzen.


Ansonsten gäbe es auch noch den Weg über ein XmlAdapter:

```
public class DoubleAdapter
    extends XmlAdapter<String, Double>
{
    public Double unmarshal(String value) {
        return Double.parseDouble(value);
    }

    public String marshal(Double value) {
        return new DecimalFormat("#.#####").format(value);
    }
}
```

Und dann wäre die Klasse sowas:

```
public class MyEntity {
    @XmlElement(name = "someValue", type = String.class, required = true)
    @XmlJavaTypeAdapter(DoubleAdapter.class);
    private double someValue;
}
```


----------



## Barista (18. Aug 2021)

Wenn es schwierig ist, in die Umwandlung von Java zu XML einzugreifen, könnte noch die Behandlung des erzeugten XML eine Lösung sein.

Suchen und Ersetzen, eventuell mit RegEx.


----------



## Wiplash4 (21. Aug 2021)

Habe es erledigt via XmlAdapter, allerdings global. Danke.


----------



## Wiplash4 (23. Aug 2021)

Anscheinend hat diese Loesung einen Hacken; der Adapter soll nur in einigen Situationen passieren. Nun wundere ich mich, ob es eine andere Loesung gibt. Einen Adapter an JAXBContext Marshaller anzuhaengen scheint nicht klappen.

[CODE lang="java" title="Adapter am Marshaller"]Marshaller marshaller = context.createMarshaller();
marshaller.setAdapter(new BigDecimalAdapter());[/CODE]

Den Adapter habe ich per Annotation auch noch an die Klasse drangeklatscht, aber ohne Wirkung.

Was mache ich falsch?


----------



## Wiplash4 (26. Aug 2021)

Kennt jemand eine Loesung, die man in xslt implementieren kann?

Kennt jemand eine Loesung, die man auf eine Klasse beschraenken kann, also die keine package-info.java braucht?


----------



## kneitzel (26. Aug 2021)

Wie das in xslt gehen müsste wurde doch in #3 bereits angerissen. Hast Du damit denn schon irgendwas probiert?


----------



## Wiplash4 (26. Aug 2021)

Ja und es hat nicht geklappt. Es gab da einige Probleme, aber eines davon schien, dass die Anzahl and Stellen irgendwie limitert ist.


----------



## kneitzel (26. Aug 2021)

Wiplash4 hat gesagt.:


> Ja und es hat nicht geklappt. Es gab da einige Probleme, aber eines davon schien, dass die Anzahl and Stellen irgendwie limitert ist.


Da wäre halt immer wichtig, Details zu erfahren. Was genau wurde wie probiert? Was war das Ergebnis?

Und bei der Serialisierung einzelner Klassen hatte ich ja als eine Option vorgeschlagen, dass über eine dedizierte Methode zu machen und das eigentliche Attribute auf Transient zu setzen. Hast Du das auch mal probiert?

Und dann wäre eine gute Idee evtl. ein kleines, ausführbares Beispiel zu erstellen anhand dessen man den aktuellen Aufbau nachvollziehen kann und dann auch Lösungsideen ausprobieren kann.


----------



## Wiplash4 (27. Aug 2021)

Anbei ein kleines Beispielprogramm, bei mir hat es noch geklappt.


----------



## Wiplash4 (28. Aug 2021)

Ich waere aber breit fuer eine Idee, bei der keine package-info.java verwendet wird, sondern bei JAXBContext und dem Marshaller etwas veraendert wird.


----------



## Oneixee5 (29. Aug 2021)

Wiplash4 hat gesagt.:


> Ja und es hat nicht geklappt. Es gab da einige Probleme, aber eines davon schien, dass die Anzahl and Stellen irgendwie limitert ist.


Wenn die Formatierung nicht wie gewünscht funktioniert (was ich nachvollziehen kann), dann könnte man folgendes versuchen:


```
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<Article>
  <Title>My Article</Title>
  <Authors>
    <Author>Mr. Foo</Author>
    <Author>Mr. Bar</Author>
  </Authors>
  <Price>4.00010</Price>
  <Body>This is my article text.</Body>
</Article>
```


```
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/">
    Article - <xsl:value-of select="/Article/Title"/>
    Price: <xsl:call-template name="Price">
      <xsl:with-param name="input" select="/Article/Price"/>
    </xsl:call-template>
    Authors: <xsl:apply-templates select="/Article/Authors/Author"/>
</xsl:template>

<xsl:template match="Author">
    -<xsl:value-of select="." />
</xsl:template>

<xsl:template name="Price">
  <xsl:param name="input"/>
  <xsl:choose>
    <xsl:when test="contains($input, '.') and substring($input, string-length($input), 1) = '0'">
        <xsl:call-template name="Price">
            <xsl:with-param name="input" select="substring($input, 0, string-length($input))"/>
        </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
        <xsl:value-of select="$input"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>
```


```
$ xsltproc test.xsl test.xml

    Article - My Article
    Price: 4.0001
    Authors:
    -Mr. Foo
    -Mr. Bar
```


----------



## Wiplash4 (30. Aug 2021)

Wie schon mal erwähnt, weiß ich nicht, wie die Tagnames lauten! Daher reicht der Hinweis auf Price nicht!


----------



## Oneixee5 (30. Aug 2021)

Die Tagnamen spielen in dem Beispiel keine Rolle. Man kann das Template ja auch einfach für jeden Tag durchlaufen. Entscheidend ist hier, was dem Parameter input übergeben wird.


----------



## Wiplash4 (30. Aug 2021)

Ich kenne die Tagnamen aber nicht! Ich habe das Gefühl, dass wir hier einander vorbeireden.
Ich kenne die Tagnamen nicht, ich kenne die Struktur der Klasse nicht und ich weiss nicht was sonst noch in der Klasse ist. Ich weiss nur, dass ich alle BigDecimals und Doubles der Art formatiereren will, dass
50.0 -> 50
formatiert wird.

[CODE lang="java" title="Code"]String xml =
"<message>
<value1>50.0</value1>
<value2>Test</value2>
</message>
"
executeXSLT(xml, "InputCleansing.xsl");
System.out.println(xml);[/CODE]

[CODE lang="xml" title="Ergebnis" highlight="2"]<message>
<value1>50</value1>
<value2>Test</value2>
</message>[/CODE]
. Ich kenne die genaue Strukture von message nicht!


----------



## mrBrown (30. Aug 2021)

Adapter „global“ funktionierte, an der Klasse oder direkt über den Marshaller allerdings nicht?


----------



## Wiplash4 (30. Aug 2021)

Nein. Sobald ich den Adapter als Annotation an die Klasse klatche, funktioniert er nicht. Hätte aber auch nichts gegen eine Lösung wie
[CODE lang="java" title="Lösung an jaxbContext"]jaxbContext.setAdapter(new BigDecimalAdapter());[/CODE]
. Aber auch das klappt nicht.


----------



## mrBrown (30. Aug 2021)

Wiplash4 hat gesagt.:


> Habe es erledigt via XmlAdapter, allerdings global. Danke.


Also stimmt das hier gar nicht?


----------



## Wiplash4 (30. Aug 2021)

Doch, es funktioniert mittels package-info.java. Aber ich versuche es an eine einzelne Klasse zu klatschen und nicht an das ganze Package!


----------

