Al usar XStream y JsonHierarchicalStreamDriver para generar valores, ¿cómo redondeo los dobles?

Aquí hay una pregunta similar y, de hecho, he intentado usar convertidores, como sugiere la respuesta.

Formato de valores decimales para xml

Estoy serializando objetos tanto para xml como para json. Estoy usando XStream y JsonHierarchicalStreamDriver cuando sea necesario.

Los objetos tienen unas propiedades de tipo doble. Estos estaban serializando bien. En xml se serializa como:

3.14159265

En json fue:

value: 3.14159265

Ahora tengo el requisito de redondear el valor a un número dado de decimales.

En mi primer intento, escribí un Convertidor, que redondea todos los dobles a 2 decimales. Se procesa como se indicó anteriormente (excepto que el valor fue redondeado), pero el problema es que las diferentes propiedades deben redondearse a diferentes cifras significativas, y el convertidor no tiene conocimiento de cuántas posiciones decimales cuando está en el método "marshal".

Mi segundo intento utiliza un objeto personalizado para reemplazar el doble, que contiene el doble objeto y el número de posiciones decimales. Se requiere otro convertidor, y esto funciona bien para el xml, pero en json representa el valor como una cadena (es decir, entre comillas) no como un número (que es lo que realmente quiero).

¿Cómo hago el doble redondeado como un número?

Gracias

1
@DenisTulskiy agradece, pero cada instancia puede tener un número diferente de lugares decimales. Si hubiera una forma de pasar el objeto actual, o el "número de posiciones decimales" actual al convertidor, eso resolvería mi problema ...
agregado el autor zod, fuente
Un vistazo rápido al tutorial de xstream converters muestra este ejemplo: "@XStreamConverter (value = BooleanConverter.class, booleans = {false}, strings = {" yes "," no "})". Así que parece que puedes darle algunos parámetros extra al convertidor. puedes usar estos?
agregado el autor Denis Tulskiy, fuente
Creo que esta tarea va mucho más allá de lo que cualquier biblioteca de clasificación haría.
agregado el autor Denis Tulskiy, fuente

1 Respuestas

Si alguien está interesado, parece que JsonHierarchicalStreamDriver usa una clase llamada JsonWriter. Entre ellos, asumen cualquier cosa que los dobles, enteros, etc. se traduzcan al número JSON, mientras que los objetos personalizados se traducirán como texto (es decir, se incluirán entre comillas). No parece de todos modos especificar cómo desea que se represente el valor.

Hay varias opciones si todavía lo necesitas hacer. Es posible que pueda utilizar una alternativa a JsonHierarchicalStreamDriver. La documentación implica que una versión de JettisonMappedXmlDriver convertirá todos los números como cadenas en números JSON (no siempre se desea).

Fui con el siguiente truco sucio:

Crea un nuevo escritor que te permita dejar comillas:

public static class HackJsonWriter extends JsonWriter {
    public HackJsonWriter(Writer writer) {
        super(writer);
    }

    private boolean dropQuotes = false;

    public void dropQuotes() {
        dropQuotes = true;
    }

    public void allowQuotes() {
        dropQuotes = false;
    }

    @Override
    protected void addValue(String value, AbstractJsonWriter.Type type)
    {
        if (dropQuotes) {
            super.addValue(value, AbstractJsonWriter.Type.NULL);
        }
        else {
            super.addValue(value, type);
        }
    }
}

Sobrescribe la creación del escritor de la siguiente manera:

new XStream(new JsonHierarchicalStreamDriver() {
    @Override
    public HierarchicalStreamWriter createWriter(Writer writer) {
        return new HackJsonWriter(writer);
    }
});

Crea un convertidor que detecte el hack y lo maneje.

public static class HackRoundedDoubleConverter implements Converter {

    private HackRoundedDoubleConverter() {
    }

    @Override
    public boolean canConvert(Class clazz) {
        return RoundedDouble.class.isAssignableFrom(clazz);
    }

    @Override
    public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
        if (value == null) {
            return;//now that i think about it, this should probably write "null" (no quotes), but I was handling that elsewhere
        }
        RoundedDouble number = (RoundedDouble)value;
        if (Double.isNaN(number.getValue()) || Double.isInfinite(number.getValue())) {
            return;//at this point, we would have a problem.  an empty node will be created no matter what (and it translates to an empty object in javascript).
        }

        if (writer.underlyingWriter() instanceof HackJsonWriter) {
            HackJsonWriter hackWriter = (HackJsonWriter)writer.underlyingWriter();
            hackWriter.dropQuotes();
            writer.setValue(number.getRoundedValue());
            hackWriter.allowQuotes();
        }
        else {
            writer.setValue(number.getRoundedValue());
        }
    }

    @Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
       //only interested in the one direction
        return null;
    }
}

Registrar el convertidor:

    xstream.registerConverter(new HackRoundedDoubleConverter());

RoundedDouble es una clase que tiene dos propiedades, un valor doble y un número de decimales.

Como dije, es un truco (es extremadamente frágil y probablemente tenga muchos problemas que ni siquiera he visto), y probablemente violé la ley al descubrir cómo hacerlo. Probablemente ni siquiera lo use, pero responde a mi pregunta original.

0
agregado
JavaScript - Comunidad española
JavaScript - Comunidad española
4 de los participantes

En este grupo hablamos de JavaScript. Partner: es.switch-case.com