xslt zum Entfernen von unnoetigen Nullkommastellen bei double und float

Wiplash4

Aktives Mitglied
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?
 
Ich habe es jetzt nicht ausprobiert, aber format-number() kann Zahlen formatieren und existiert schon ab XSLT 1.0:

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).
 
Zuletzt bearbeitet von einem Moderator:
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.
 
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!
 
Danke kneitzel, allerdings wuerde das fuer mich nicht funktionieren. Ist es moeglich das irgendwie ueber das JAXBContext zu machen? Vielleicht mit irgendeinem Flag?
 
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.#####')"/>
 
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.
 
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:
Java:
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:
Java:
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:
Java:
public class MyEntity {
    @XmlElement(name = "someValue", type = String.class, required = true)
    @XmlJavaTypeAdapter(DoubleAdapter.class);
    private double someValue;
}
 
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.
 
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?
 
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?
 
Wie das in xslt gehen müsste wurde doch in #3 bereits angerissen. Hast Du damit denn schon irgendwas probiert?
 
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.
 
Ich waere aber breit fuer eine Idee, bei der keine package-info.java verwendet wird, sondern bei JAXBContext und dem Marshaller etwas veraendert wird.
 
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:
<?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:
<?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>
Bash:
$ xsltproc test.xsl test.xml

    Article - My Article
    Price: 4.0001
    Authors:
    -Mr. Foo
    -Mr. Bar
 
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.
 
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!
 
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.
 
Doch, es funktioniert mittels package-info.java. Aber ich versuche es an eine einzelne Klasse zu klatschen und nicht an das ganze Package!
 

Zurück
Oben