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

com.teamscale.commons.service.XmlSerializationUtils Maven / Gradle / Ivy

There is a newer version: 2025.1.0-rc2
Show newest version
/*
 * Copyright (c) CQSE GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.teamscale.commons.service;

import java.io.IOException;
import java.util.Collection;
import java.util.Map;

import org.conqat.engine.commons.util.JsonUtils;
import org.conqat.lib.commons.assertion.CCSMAssert;
import org.conqat.lib.commons.string.StringUtils;

import com.ctc.wstx.api.InvalidCharHandler;
import com.ctc.wstx.api.WstxInputProperties;
import com.ctc.wstx.api.WstxOutputProperties;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationModule;

/**
 * Utility code for serializing to XML.
 */
public class XmlSerializationUtils {

	/**
	 * The shared jackson XML mapper instance. Once configured the object is
	 * thread-safe.
	 */
	private static final XmlMapper XML_MAPPER;

	static {
		XML_MAPPER = XmlMapper.builder() //
				.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS) //
				.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) //
				.visibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) //
				.build();
		XML_MAPPER.registerModule(new JakartaXmlBindAnnotationModule());
		// remove any entities that are not allowed in XML 1.0 and replace with
		// "?". In most cases where we use this code, this will not cause any
		// problems.
		if (XML_MAPPER.getFactory().getXMLOutputFactory()
				.isPropertySupported(WstxOutputProperties.P_OUTPUT_INVALID_CHAR_HANDLER)) {
			XML_MAPPER.getFactory().getXMLOutputFactory().setProperty(
					WstxOutputProperties.P_OUTPUT_INVALID_CHAR_HANDLER, new InvalidCharHandler.ReplacingHandler('?'));
		}
		if (XML_MAPPER.getFactory().getXMLInputFactory()
				.isPropertySupported(WstxInputProperties.P_ALLOW_XML11_ESCAPED_CHARS_IN_XML10)) {
			XML_MAPPER.getFactory().getXMLInputFactory()
					.setProperty(WstxInputProperties.P_ALLOW_XML11_ESCAPED_CHARS_IN_XML10, true);
		}
	}

	/**
	 * Serializes the given object to XML. This will throw an
	 * {@link org.conqat.engine.commons.util.JsonUtils.JsonSerializationRuntimeException}
	 * when serialization fails.
	 */
	public static String serializeToXML(Object object) {
		try {
			return XML_MAPPER.writeValueAsString(object);
		} catch (JsonProcessingException e) {
			throw new JsonUtils.JsonSerializationRuntimeException(e);
		}
	}

	/**
	 * Deserializes the given object from XML.
	 * 
	 * This will fail with an assertion error if the given expectedClass implements
	 * Map or Collection. This XML deserialization method can't reconstruct the
	 * element types of these collections and therefore would fail to reconstruct
	 * the original object. You need to specify the exact expected type using
	 * {@link #deserializeFromXML(String, JavaType)}.
	 */
	public static  T deserializeFromXML(String xml, Class expectedClass) throws IOException {
		if (Map.class.isAssignableFrom(expectedClass) || Collection.class.isAssignableFrom(expectedClass)) {
			CCSMAssert.fail(
					"Tried to deserialize with a Collection/Map class. This will fail if the elements of the class (generic types) are any other than String. Construct and pass a more detailed JavaType instead.");
		}
		return deserializeFromXML(xml, TypeFactory.defaultInstance().constructType(expectedClass));
	}

	/**
	 * Deserializes the given object from XML.
	 * 
	 * Generate the expected {@link JavaType} using methods in {@link JsonUtils} or
	 * using {@link TypeFactory}. For example:
	 * TypeFactory.defaultInstance().constructMapType(Map.class, KeyType.class, ValueType.class)
	 */
	public static  T deserializeFromXML(String xml, JavaType expectedType) throws IOException {
		return XML_MAPPER.readValue(xml, expectedType);
	}

	/** Parses the given XML string to a JSON node without validation. */
	public static JsonNode parseXmlToJsonNode(String xml) throws IOException {
		return XML_MAPPER.readTree(StringUtils.stringToBytes(xml));
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy