Mam dość złożony obiekt drzewa Jaxb. Dla każdy węzeł liścia , muszę filtruj swoją rzeczywistą wartość

Na przykład.

<Book>
    <Title>Yogasana Vijnana: the Science of Yoga</Title>
    <Author>Dhirendra Brahmachari</Author>
    <Date>1966</Date>
</Book>

Węzły liści tutaj byłyby Title, author i Date.
wyobraź sobie, że potrzebuję marshalled dokumentu dla tego modelu JaxB z pierwszym znakiem usuniętym dla każdego węzła liścia:

<Book>
    <Title>ogasana Vijnana: the Science of Yoga</Title>
    <Author>hirendra Brahmachari</Author>
    <Date>966</Date>
</Book>


.. Jakie jest najlepsze podejście?
Widzę jednak dwa punkty wyjścia, jednak utknąłem.

1. Wykonaj zmianę modelu JaxB
Czy jest jakiś machizm przechodzący, którego mogę użyć, aby uzyskać elementy liści dowolnego obiektu JaxB (jakiś rodzaj wzoru gościa lub czegoś)?

2. Hak do wysiłku
Może możemy zaczepić się do Marshalling, np. Korzystanie z XMLStreamWriter ..

Czy istnieje elegancki rozwiązanie dla tego rodzaju problemu?

8
MRalwasser 17 luty 2017, 18:02

2 odpowiedzi

Najlepsza odpowiedź

Innym podejściem oparte na Decorator typu XMLStreamWriter, który będzie po prostu pominąć pierwszym znakiem treści tekstowej, jednak nie będziesz miał możliwości ograniczenia go tylko do węzłów liści, będzie miał zastosowanie Ta sama logika do wszystkich zawartości tekstu nie tylko węzłów liści, które nie będzie problemem, jeśli Twoje marszowanie nie wygeneruje mieszanych treści takich jak w swoim przykładzie. Rzeczywiście, jeśli nie masz mieszanej zawartości (treść tekstu i węzłów mieszanych razem), tylko węzły liści mogą mieć zawartość tekstu.

Twój dekorator może być taki:

public class RemoveFirstCharacter implements XMLStreamWriter {

    private final XMLStreamWriter delegate;

    public RemoveFirstCharacter(final XMLStreamWriter delegate) {
        this.delegate = delegate;
    }

    @Override
    public void writeStartElement(final String localName) throws XMLStreamException {
        delegate.writeStartElement(localName);
    }

    @Override
    public void writeStartElement(final String namespaceURI, final String localName) 
        throws XMLStreamException {
        delegate.writeStartElement(namespaceURI, localName);
    }

    ...

    @Override
    public void writeCharacters(final String text) throws XMLStreamException {
        // Skip the first character
        delegate.writeCharacters(text.substring(1));
    }

    @Override
    public void writeCharacters(final char[] text, final int start, final int len)
        throws XMLStreamException {
        if (start == 0) {
            // Skip the first character
            delegate.writeCharacters(text, 1, len - 1);
        } else {
            delegate.writeCharacters(text, start, len);
        }
    }
}

Wtedy twój kod byłby:

// Create the marshaller for the class Book
JAXBContext jaxbContext = JAXBContext.newInstance(Book.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

// Create the main XMLStreamWriter
XMLOutputFactory output = XMLOutputFactory.newInstance();
XMLStreamWriter writer = output.createXMLStreamWriter(System.out);

// Apply the custom XMLStreamWriter that will remove the first character
// of each text content
jaxbMarshaller.marshal(book, new RemoveFirstCharacter(writer));
11
Nicolas Filotto 25 luty 2017, 16:22

Możesz po procesie wynikowy XML, aby usunąć pierwszy znak zawartości tekstu każdego węzła liści przy użyciu XSLT z następnym arkuszem stylów:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <!-- For each text content of leaf nodes (nodes without child nodes) -->
    <xsl:template match="*[not(*)]/text()">
        <!-- Skip the first character -->
        <xsl:value-of select="substring(., 2)"/>
    </xsl:template>
</xsl:stylesheet>

Zgodnie z wielkością wyniku {X0}} można zachować wynik do pamięci przed zastosowaniem arkuszy stylów lub zapisać najpierw wynikowy XML do pliku tymczasowego.

Oto, jak można założyć, że wynikowy XML może pasować do pamięci:

// Create the marshaller for the class Book
JAXBContext jaxbContext = JAXBContext.newInstance(Book.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

// Make the output being pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

// Marshall the book instance and keep the result into a ByteArrayOutputStream
ByteArrayOutputStream out = new ByteArrayOutputStream();
jaxbMarshaller.marshal(book, out);

TransformerFactory factory = TransformerFactory.newInstance();
// Define the stylesheet to apply
Transformer transformer = factory.newTransformer(new StreamSource(xsltFile));
// Define the input XML content
Source text = new StreamSource(new ByteArrayInputStream(out.toByteArray()));
// Apply the stylesheet and store the content into outputFile
transformer.transform(text, new StreamResult(outputFile));

Wynik:

<?xml version="1.0" encoding="UTF-8"?>
<Book>
    <Title>ogasana Vijnana: the Science of Yoga</Title>
    <Author>hirendra Brahmachari</Author>
    <Date>966</Date>
</Book>
6
Nicolas Filotto 21 luty 2017, 11:40