All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ebmwebsourcing.easycommons.json.Xml2JsonConverter Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2019-2023 Linagora
 * 
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the New BSD License (3-clause license).
 *
 * This program/library 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 New BSD License (3-clause license)
 * for more details.
 *
 * You should have received a copy of the New BSD License (3-clause license)
 * along with this program/library; If not, see http://directory.fsf.org/wiki/License:BSD_3Clause/
 * for the New BSD License (3-clause license).
 */
package com.ebmwebsourcing.easycommons.json;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Properties;
import java.util.logging.Logger;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import com.ebmwebsourcing.easycommons.json.exception.EasyCommonsJsonException;
import com.ebmwebsourcing.easycommons.stream.EasyByteArrayOutputStream;

import de.odysseus.staxon.json.JsonXMLConfig;
import de.odysseus.staxon.json.JsonXMLConfigBuilder;

public class Xml2JsonConverter {

    /**
     * An XSL used as custom pre-processing applied on XML source before JSON transformation
     */
    private final Templates preXmlTransformation;

    /**
     * The result of the custom pre-processing is directly JSON data instead of XML data
     */
    private final boolean isPreXmlTranformationResultJson;

    /**
     * External parameters used as XSL pararameters in the pre XML transformation.
     */
    private final Properties externalParameters;

    /**
     * Namespace of the XSL parameter names associated to the external parameters
     */
    private final String externalParametersNS;

    /**
     * The JSON output factory creating output JSON stream according to the right configuration
     */
    private final XMLOutputFactory jsonOutputStreamWriterFactory;

    public Xml2JsonConverter(final Templates preXmlTransformation, final boolean isPreXmlTranformationResultJson,
            final Properties externalParameters, final String externalParametersNS,
            final XMLOutputFactory jsonOutputStreamWriterFactory) {
        this.preXmlTransformation = preXmlTransformation;
        this.isPreXmlTranformationResultJson = isPreXmlTranformationResultJson;
        this.externalParameters = externalParameters;
        this.externalParametersNS = externalParametersNS;
        this.jsonOutputStreamWriterFactory = jsonOutputStreamWriterFactory;
    }

    public void transform(final OutputStream jsonOutputStream, final Source xmlSource, final Logger logger)
            throws EasyCommonsJsonException {

        try {
            if (this.preXmlTransformation != null) {
                // TODO: it would be nicer not to work in-memory but directly on the fly
                // but XSLT does not support streaming (well XSLT 3.0 does but it's only with saxon)
                try (final EasyByteArrayOutputStream ebaos = new EasyByteArrayOutputStream()) {
                    final Result out = new StreamResult(ebaos);
                    final Transformer transformer = this.preXmlTransformation.newTransformer();

                    synchronized (this.externalParameters) {
                        for (final Entry placeholder : this.externalParameters.entrySet()) {
                            transformer.setParameter(
                                    new QName(this.externalParametersNS, (String) placeholder.getKey()).toString(),
                                    placeholder.getValue());
                        }
                    }

                    transformer.transform(xmlSource, out);

                    logger.fine("Pre-processing applied on XML source.");
                    if (this.isPreXmlTranformationResultJson) {
                        jsonOutputStream.write(ebaos.getBuf());
                    } else {
                        JSONHelper.convertXMLToJSON(new StreamSource(ebaos.toByteArrayInputStream()), jsonOutputStream,
                                this.jsonOutputStreamWriterFactory);
                    }
                }
            } else {
                JSONHelper.convertXMLToJSON(xmlSource, jsonOutputStream, this.jsonOutputStreamWriterFactory);
                logger.fine("No pre-processing applied on XML source.");
            }
        } catch (final TransformerException | XMLStreamException | IOException e) {
            throw new EasyCommonsJsonException(e);
        }
    }

    /**
     * Create the XML 2 JSON transformation configuration
     * 
     * @param virtRoot
     *            If needed, this XML element will enclosed all converted JSON attributes. Can be {@code null}.
     * @param multiplePi
     *            Not {@code null}.
     * @param namespaceMappings
     *            Namespace mapping definitions. Map key are XML predix and map value are associated namespaces. Can be
     *            {@code null}
     * @return
     */
    public static final JsonXMLConfig buildConfiguration(final QName virtRoot, final Optional multiplePi,
            final Map namespaceMappings) {
        final JsonXMLConfigBuilder config = new JsonXMLConfigBuilder();

        if (virtRoot != null) {
            config.virtualRoot(virtRoot);
        }

        if (multiplePi.isPresent()) {
            config.multiplePI(multiplePi.get());
        } else {
            config.multiplePI(false);
        }

        if (namespaceMappings != null) {
            for (final Entry entry : namespaceMappings.entrySet()) {
                config.namespaceMapping(entry.getKey(), entry.getValue());
            }
        }

        return config.build();
    }

    public void setExternalParameters(final Properties externalParameters) {
        synchronized (this.externalParameters) {
            this.externalParameters.clear();
            this.externalParameters.putAll(externalParameters);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy