org.eclipse.persistence.jaxb.JAXBMarshaller Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
// Marcel Valovy - 2.6.0 - added case insensitive unmarshalling property,
// added Bean Validation support.
package org.eclipse.persistence.jaxb;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.lang.reflect.Array;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.MarshalException;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.PropertyException;
import jakarta.xml.bind.ValidationEventHandler;
import jakarta.xml.bind.annotation.adapters.XmlAdapter;
import jakarta.xml.bind.attachment.AttachmentMarshaller;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Result;
import javax.xml.validation.Schema;
import org.eclipse.persistence.core.queries.CoreAttributeGroup;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.BeanValidationException;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.jaxb.ObjectGraphImpl;
import org.eclipse.persistence.internal.jaxb.WrappedValue;
import org.eclipse.persistence.internal.jaxb.many.ManyValue;
import org.eclipse.persistence.internal.localization.JAXBLocalization;
import org.eclipse.persistence.internal.oxm.Constants;
import org.eclipse.persistence.internal.oxm.Root;
import org.eclipse.persistence.internal.oxm.record.namespaces.MapNamespacePrefixMapper;
import org.eclipse.persistence.internal.oxm.record.namespaces.NamespacePrefixMapperWrapper;
import org.eclipse.persistence.jaxb.JAXBContext.RootLevelXmlAdapter;
import org.eclipse.persistence.jaxb.attachment.AttachmentMarshallerAdapter;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.LogLevel;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.oxm.CharacterEscapeHandler;
import org.eclipse.persistence.oxm.JSONWithPadding;
import org.eclipse.persistence.oxm.MediaType;
import org.eclipse.persistence.oxm.NamespacePrefixMapper;
import org.eclipse.persistence.oxm.XMLMarshalListener;
import org.eclipse.persistence.oxm.XMLMarshaller;
import org.eclipse.persistence.oxm.record.MarshalRecord;
import org.eclipse.persistence.oxm.record.XMLEventWriterRecord;
import org.eclipse.persistence.oxm.record.XMLStreamWriterRecord;
import org.w3c.dom.Node;
import org.xml.sax.ContentHandler;
/**
* INTERNAL:
*
* Purpose:To Provide an implementation of the JAXB 2.0 Marshaller
* Interface
*
* Responsibilities:
*
* - Provide a JAXB wrapper on the XMLMarshaller API
* - Perform Object to XML Conversions
*
* This implementation of the JAXB 2.1/2.2 Marshaller interface provides the
* required functionality by acting as a thin wrapper on the existing
* XMLMarshaller API.
*
* @author mmacivor
* @since Oracle TopLink 11.1.1.0.0
* @see jakarta.xml.bind.Marshaller
* @see org.eclipse.persistence.jaxb.MarshallerProperties
* @see org.eclipse.persistence.oxm.XMLMarshaller
*/
public class JAXBMarshaller implements jakarta.xml.bind.Marshaller {
private JAXBBeanValidator beanValidator;
private BeanValidationMode beanValidationMode;
// The actual type is ValidatorFactory. It's done due to optional nature of jakarta.validation.
private Object prefValidatorFactory;
private boolean bvNoOptimisation = false;
private Class>[] beanValidationGroups;
private final XMLMarshaller xmlMarshaller;
private final JAXBContext jaxbContext;
private ValidationEventHandler validationEventHandler;
public static final String XML_JAVATYPE_ADAPTERS = "xml-javatype-adapters";
private static final String SUN_NAMESPACE_PREFIX_MAPPER = "org.glassfish.jaxb.namespacePrefixMapper";
private static final String SUN_JSE_NAMESPACE_PREFIX_MAPPER = "com.sun.xml.internal.bind.namespacePrefixMapper";
private static final String SUN_INDENT_STRING = "org.glassfish.jaxb.indentString";
private static final String SUN_JSE_INDENT_STRING = "com.sun.xml.internal.bind.indentString";
private static final String SUN_CHARACTER_ESCAPE_HANDLER_MARSHALLER = "org.glassfish.jaxb.marshaller.CharacterEscapeHandler";
private static final String SUN_JSE_CHARACTER_ESCAPE_HANDLER_MARSHALLER = "com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler";
private static final String SUN_CHARACTER_ESCAPE_HANDLER= "org.glassfish.jaxb.characterEscapeHandler";
private static final String SUN_JSE_CHARACTER_ESCAPE_HANDLER = "com.sun.xml.internal.bind.characterEscapeHandler";
// XML_DECLARATION is the "opposite" to JAXB_FRAGMENT. If XML_DECLARATION is set to false it means JAXB_FRAGMENT should be set to true.
private static final String XML_DECLARATION = "org.glassfish.jaxb.xmlDeclaration";
private static final String XML_HEADERS = "org.glassfish.jaxb.xmlHeaders";
private static final String OBJECT_IDENTITY_CYCLE_DETECTION = "org.glassfish.jaxb.objectIdentitityCycleDetection";
/**
* This constructor initializes various settings on the XML marshaller.
*
* @param newXMLMarshaller xml marshaller
* @param jaxbContext jaxb context
*/
public JAXBMarshaller(XMLMarshaller newXMLMarshaller, JAXBContext jaxbContext) {
this.jaxbContext = jaxbContext;
validationEventHandler = JAXBContext.DEFAULT_VALIDATION_EVENT_HANDLER;
beanValidationMode = BeanValidationMode.AUTO;
if (BeanValidationChecker.isBeanValidationPresent()) {
beanValidator = JAXBBeanValidator.getMarshallingBeanValidator(jaxbContext);
}
xmlMarshaller = newXMLMarshaller;
xmlMarshaller.setErrorHandler(new JAXBErrorHandler(validationEventHandler));
xmlMarshaller.setEncoding("UTF-8");
xmlMarshaller.setFormattedOutput(false);
xmlMarshaller.getProperties().put(Constants.JAXB_MARSHALLER, this);
}
/**
* Create an instance of XMLRoot populated from the contents of the provided
* JAXBElement. XMLRoot will be used to hold the contents of the JAXBElement
* while the marshal operation is performed by TopLink OXM. This will avoid
* adding any runtime dependencies to TopLink.
*
* @param elt
* @return
*/
private Root createXMLRootFromJAXBElement(JAXBElement elt) {
// create an XMLRoot to hand into the marshaller
Root xmlroot = new Root();
Object objectValue = elt.getValue();
xmlroot.setObject(objectValue);
QName qname = elt.getName();
xmlroot.setLocalName(qname.getLocalPart());
xmlroot.setNamespaceURI(qname.getNamespaceURI());
xmlroot.setDeclaredType(elt.getDeclaredType());
xmlroot.setNil(elt.isNil());
if (elt.getDeclaredType() == CoreClassConstants.ABYTE || elt.getDeclaredType() == CoreClassConstants.APBYTE ||
elt.getDeclaredType().getCanonicalName().equals("jakarta.activation.DataHandler") ||
elt.getDeclaredType().isEnum()) {
// need a binary data mapping so need to wrap
Class generatedClass = getClassToGeneratedClasses().get(elt.getDeclaredType().getCanonicalName());
if(!elt.getDeclaredType().isEnum()) {
xmlroot.setSchemaType(Constants.BASE_64_BINARY_QNAME);
}
if (generatedClass != null && WrappedValue.class.isAssignableFrom(generatedClass)) {
ClassDescriptor desc = xmlMarshaller.getXMLContext().getSession(generatedClass).getDescriptor(generatedClass);
Object newObject = desc.getInstantiationPolicy().buildNewInstance();
((WrappedValue) newObject).setValue(objectValue);
xmlroot.setObject(newObject);
return xmlroot;
}
} else {
xmlroot.setSchemaType((QName) org.eclipse.persistence.internal.oxm.XMLConversionManager.getDefaultJavaTypes().get(elt.getDeclaredType()));
}
if (elt instanceof WrappedValue) {
xmlroot.setObject(elt);
return xmlroot;
}
Map qNameToGeneratedClasses = jaxbContext.getQNameToGeneratedClasses();
if (qNameToGeneratedClasses != null) {
Class theClass = qNameToGeneratedClasses.get(qname);
if (theClass != null && WrappedValue.class.isAssignableFrom(theClass)) {
ClassDescriptor desc = xmlMarshaller.getXMLContext().getSession(theClass).getDescriptor(theClass);
Object newObject = desc.getInstantiationPolicy().buildNewInstance();
((WrappedValue) newObject).setValue(objectValue);
xmlroot.setObject(newObject);
return xmlroot;
}
}
Class generatedClass = null;
if (jaxbContext.getTypeMappingInfoToGeneratedType() != null) {
if (jaxbContext.getTypeToTypeMappingInfo() != null) {
if (elt.getDeclaredType() != null && elt.getDeclaredType().isArray()) {
TypeMappingInfo tmi = jaxbContext.getTypeToTypeMappingInfo().get(elt.getDeclaredType());
generatedClass = jaxbContext.getTypeMappingInfoToGeneratedType().get(tmi);
} else if (elt instanceof JAXBTypeElement) {
Type objectType = ((JAXBTypeElement) elt).getType();
TypeMappingInfo tmi = jaxbContext.getTypeToTypeMappingInfo().get(objectType);
generatedClass = jaxbContext.getTypeMappingInfoToGeneratedType().get(tmi);
}
}
} else {
if (elt.getDeclaredType() != null && elt.getDeclaredType().isArray()) {
if (jaxbContext.getArrayClassesToGeneratedClasses() != null) {
generatedClass = jaxbContext.getArrayClassesToGeneratedClasses().get(elt.getDeclaredType().getCanonicalName());
}
} else if (elt instanceof JAXBTypeElement) {
Type objectType = ((JAXBTypeElement) elt).getType();
generatedClass = jaxbContext.getCollectionClassesToGeneratedClasses().get(objectType);
}
}
if (generatedClass != null) {
ClassDescriptor desc = xmlMarshaller.getXMLContext().getSession(generatedClass).getDescriptor(generatedClass);
Object newObject = desc.getInstantiationPolicy().buildNewInstance();
((ManyValue) newObject).setItem(objectValue);
xmlroot.setObject(newObject);
}
return xmlroot;
}
@Override
public XmlAdapter getAdapter(Class javaClass) {
HashMap result = (HashMap) xmlMarshaller.getProperty(XML_JAVATYPE_ADAPTERS);
if (result == null) {
return null;
}
return (XmlAdapter) result.get(javaClass);
}
@Override
public AttachmentMarshaller getAttachmentMarshaller() {
if (xmlMarshaller.getAttachmentMarshaller() == null) {
return null;
}
return ((AttachmentMarshallerAdapter) xmlMarshaller.getAttachmentMarshaller()).getAttachmentMarshaller();
}
@Override
public ValidationEventHandler getEventHandler() throws JAXBException {
return validationEventHandler;
}
@Override
public Marshaller.Listener getListener() {
XMLMarshalListener xmlMarshalListener = xmlMarshaller.getMarshalListener();
if(null != xmlMarshalListener) {
return ((JAXBMarshalListener) xmlMarshalListener).getListener();
}
return null;
}
@Override
public Node getNode(Object object) throws JAXBException {
throw new UnsupportedOperationException();
}
/**
* Get a property from the JAXBMarshaller. Attempting to get any unsupported
* property will result in a jakarta.xml.bind.PropertyException
* @see org.eclipse.persistence.jaxb.MarshallerProperties
*/
@Override
public Object getProperty(String key) throws PropertyException {
if (key == null) {
throw new IllegalArgumentException();
} else if (JAXB_FORMATTED_OUTPUT.equals(key)) {
return xmlMarshaller.isFormattedOutput();
} else if (JAXB_ENCODING.equals(key)) {
return xmlMarshaller.getEncoding();
} else if (JAXB_SCHEMA_LOCATION.equals(key)) {
return xmlMarshaller.getSchemaLocation();
} else if (JAXB_NO_NAMESPACE_SCHEMA_LOCATION.equals(key)) {
return xmlMarshaller.getNoNamespaceSchemaLocation();
} else if (Constants.JAXB_FRAGMENT.equals(key)) {
return xmlMarshaller.isFragment();
} else if (MarshallerProperties.MEDIA_TYPE.equals(key)) {
return xmlMarshaller.getMediaType();
} else if (MarshallerProperties.NAMESPACE_PREFIX_MAPPER.equals(key)) {
return xmlMarshaller.getNamespacePrefixMapper();
} else if (MarshallerProperties.INDENT_STRING.equals(key) || SUN_INDENT_STRING.equals(key) || SUN_JSE_INDENT_STRING.equals(key)) {
return xmlMarshaller.getIndentString();
} else if (MarshallerProperties.CHARACTER_ESCAPE_HANDLER.equals(key)) {
return xmlMarshaller.getCharacterEscapeHandler();
} else if (XML_DECLARATION.equals(key)) {
return !xmlMarshaller.isFragment();
} else if (XML_HEADERS.equals(key)) {
return xmlMarshaller.getXmlHeader();
} else if (OBJECT_IDENTITY_CYCLE_DETECTION.equals(key)) {
return xmlMarshaller.isEqualUsingIdenity();
} else if (MarshallerProperties.JSON_ATTRIBUTE_PREFIX.equals(key)) {
return xmlMarshaller.getAttributePrefix();
} else if (MarshallerProperties.JSON_INCLUDE_ROOT.equals(key)) {
return xmlMarshaller.isIncludeRoot();
} else if (MarshallerProperties.JSON_VALUE_WRAPPER.equals(key)) {
return xmlMarshaller.getValueWrapper();
} else if (MarshallerProperties.JSON_NAMESPACE_SEPARATOR.equals(key)) {
return xmlMarshaller.getNamespaceSeparator();
} else if (MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME.equals(key)) {
return xmlMarshaller.isWrapperAsCollectionName();
} else if (MarshallerProperties.JSON_USE_XSD_TYPES_WITH_PREFIX.equals(key)) {
return xmlMarshaller.getJsonTypeConfiguration().isUseXsdTypesWithPrefix();
} else if (MarshallerProperties.JSON_TYPE_COMPATIBILITY.equals(key)) {
return xmlMarshaller.getJsonTypeConfiguration().isJsonTypeCompatibility();
} else if (MarshallerProperties.JSON_TYPE_ATTRIBUTE_NAME.equals(key)) {
return xmlMarshaller.getJsonTypeConfiguration().getJsonTypeAttributeName();
} else if (MarshallerProperties.JSON_DISABLE_NESTED_ARRAY_NAME.equals(key)) {
return xmlMarshaller.getJsonTypeConfiguration().isJsonDisableNestedArrayName();
} else if (SUN_CHARACTER_ESCAPE_HANDLER.equals(key) || SUN_JSE_CHARACTER_ESCAPE_HANDLER.equals(key) ||SUN_CHARACTER_ESCAPE_HANDLER_MARSHALLER.equals(key) || SUN_JSE_CHARACTER_ESCAPE_HANDLER_MARSHALLER.equals(key)) {
if (xmlMarshaller.getCharacterEscapeHandler() instanceof CharacterEscapeHandlerWrapper) {
CharacterEscapeHandlerWrapper wrapper = (CharacterEscapeHandlerWrapper) xmlMarshaller.getCharacterEscapeHandler();
return wrapper.getHandler();
}
return xmlMarshaller.getCharacterEscapeHandler();
} else if (SUN_NAMESPACE_PREFIX_MAPPER.equals(key) || SUN_JSE_NAMESPACE_PREFIX_MAPPER.equals(key)) {
NamespacePrefixMapperWrapper wrapper = (NamespacePrefixMapperWrapper) xmlMarshaller.getNamespacePrefixMapper();
if(wrapper == null){
return null;
}
return wrapper.getPrefixMapper();
} else if (MarshallerProperties.OBJECT_GRAPH.equals(key)) {
Object graph = xmlMarshaller.getMarshalAttributeGroup();
if(graph instanceof CoreAttributeGroup) {
return new ObjectGraphImpl((CoreAttributeGroup)graph);
}
return graph;
} else if (MarshallerProperties.BEAN_VALIDATION_MODE.equals(key)) {
return this.beanValidationMode;
} else if (MarshallerProperties.BEAN_VALIDATION_FACTORY.equals(key)) {
return this.prefValidatorFactory;
} else if (MarshallerProperties.BEAN_VALIDATION_GROUPS.equals(key)) {
return this.beanValidationGroups;
} else if (MarshallerProperties.BEAN_VALIDATION_NO_OPTIMISATION.equals(key)) {
return this.bvNoOptimisation;
} else if (MarshallerProperties.MOXY_LOG_PAYLOAD.equals(key)) {
return xmlMarshaller.isLogPayload();
}
throw new PropertyException(key);
}
@Override
public Schema getSchema() {
return xmlMarshaller.getSchema();
}
@Override
public void marshal(Object object, ContentHandler contentHandler) throws JAXBException {
if (object == null || contentHandler == null) {
throw new IllegalArgumentException();
}
Listener listener = getListener();
if(listener != null) {
if(object instanceof JAXBElement) {
listener.beforeMarshal(object);
}
}
Object oxmObject = validateAndTransformIfNeeded(object); // xml bindings + object
try {
xmlMarshaller.marshal(oxmObject, contentHandler);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception e) {
throw new MarshalException(e);
}
if(listener != null) {
if(object instanceof JAXBElement) {
listener.afterMarshal(object);
}
}
}
private Object wrapEnumeration(Object object, Class enumerationClass) {
Class generatedClass = this.getClassToGeneratedClasses().get(enumerationClass.getName());
if (generatedClass != null && WrappedValue.class.isAssignableFrom(generatedClass)) {
ClassDescriptor desc = xmlMarshaller.getXMLContext().getSession(generatedClass).getDescriptor(generatedClass);
Object newObject = desc.getInstantiationPolicy().buildNewInstance();
((WrappedValue) newObject).setValue(object);
object = newObject;
}
return object;
}
@Override
public void marshal(Object object, XMLEventWriter eventWriter) throws JAXBException {
if (object == null || eventWriter == null) {
throw new IllegalArgumentException();
}
Listener listener = getListener();
if(listener != null) {
if(object instanceof JAXBElement) {
listener.beforeMarshal(object);
}
}
Object oxmObject = validateAndTransformIfNeeded(object); // xml bindings + object
try {
XMLEventWriterRecord record = new XMLEventWriterRecord(eventWriter);
record.setMarshaller(this.xmlMarshaller);
this.xmlMarshaller.marshal(oxmObject, record);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception ex) {
throw new MarshalException(ex);
}
if(listener != null) {
if(object instanceof JAXBElement) {
listener.afterMarshal(object);
}
}
}
public void marshal(Object object, XMLEventWriter eventWriter, TypeMappingInfo type) throws JAXBException {
if (jaxbContext.getTypeMappingInfoToGeneratedType() == null) {
marshal(object, eventWriter);
} else {
JAXBElement element = null;
Object value = object;
if (object instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
element = (JAXBElement) object;
value = element.getValue();
}
RootLevelXmlAdapter adapter = jaxbContext.getTypeMappingInfoToJavaTypeAdapters().get(type);
if (adapter != null) {
try {
value = adapter.getXmlAdapter().marshal(value);
} catch (Exception ex) {
throw new JAXBException(XMLMarshalException.marshalException(ex));
}
}
value = wrapObject(value, element, type);
marshal(value, eventWriter);
}
}
@Override
public void marshal(Object object, Node node) throws JAXBException {
if (object == null || node == null) {
throw new IllegalArgumentException();
}
Listener listener = getListener();
if(listener != null) {
if(object instanceof JAXBElement) {
listener.beforeMarshal(object);
}
}
Object oxmObject = validateAndTransformIfNeeded(object); // xml bindings + object
try {
xmlMarshaller.marshal(oxmObject, node);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception e) {
throw new MarshalException(e);
}
if(listener != null) {
if(object instanceof JAXBElement) {
listener.afterMarshal(object);
}
}
}
@Override
public void marshal(Object object, OutputStream outputStream) throws JAXBException {
if (object == null || outputStream == null) {
throw new IllegalArgumentException();
}
Listener listener = getListener();
if(listener != null) {
if(object instanceof JAXBElement) {
listener.beforeMarshal(object);
}
}
Object oxmObject = validateAndTransformIfNeeded(object); // xml bindings + object
try {
xmlMarshaller.marshal(oxmObject, outputStream);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception e) {
throw new MarshalException(e);
}
if(listener != null) {
if(object instanceof JAXBElement) {
listener.afterMarshal(object);
}
}
}
@Override
public void marshal(Object object, File file) throws JAXBException {
try {
FileOutputStream outputStream = new FileOutputStream(file);
try {
marshal(object, outputStream); // link to the other one
} finally {
outputStream.close();
}
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception ex) {
throw new MarshalException(ex);
}
}
@Override
public void marshal(Object object, Result result) throws JAXBException {
if (object == null || result == null) {
throw new IllegalArgumentException();
}
object = validateAndTransformIfNeeded(object); // xml bindings + json object
try {
xmlMarshaller.marshal(object, result);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception e) {
throw new MarshalException(e);
}
}
public void marshal(Object object, Result result, TypeMappingInfo type) throws JAXBException {
if (jaxbContext.getTypeMappingInfoToGeneratedType() == null) {
marshal(object, result);
} else {
JAXBElement element = null;
Object value = object;
if (object instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
element = (JAXBElement) object;
value = element.getValue();
}
if(jaxbContext.getTypeMappingInfoToJavaTypeAdapters().size() > 0){
RootLevelXmlAdapter adapter = jaxbContext.getTypeMappingInfoToJavaTypeAdapters().get(type);
if (adapter != null) {
try {
value = adapter.getXmlAdapter().marshal(value);
} catch (Exception ex) {
throw new JAXBException(XMLMarshalException.marshalException(ex));
}
}
}
value = wrapObject(value, element, type);
marshal(value, result);
}
}
@Override
public void marshal(Object object, XMLStreamWriter streamWriter) throws JAXBException {
if (object == null || streamWriter == null) {
throw new IllegalArgumentException();
}
Listener listener = getListener();
if(listener != null) {
if(object instanceof JAXBElement) {
listener.beforeMarshal(object);
}
}
Object oxmObject = validateAndTransformIfNeeded(object); // xml bindings + object
try {
XMLStreamWriterRecord record = new XMLStreamWriterRecord(streamWriter);
record.setMarshaller(this.xmlMarshaller);
this.xmlMarshaller.marshal(oxmObject, record);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception ex) {
throw new MarshalException(ex);
}
if(listener != null) {
if(object instanceof JAXBElement) {
listener.afterMarshal(object);
}
}
}
private Object validateAndTransformIfNeeded(Object obj) throws BeanValidationException {
Object result = modifyObjectIfNeeded(obj);
if (beanValidator != null && beanValidator.shouldValidate(obj, beanValidationMode, prefValidatorFactory, bvNoOptimisation)) {
beanValidator.validate(result, beanValidationGroups);
}
return result;
}
private Object modifyObjectIfNeeded(Object obj) {
if (obj instanceof Collection) {
Collection objectList = (Collection) obj;
List newList = new ArrayList(objectList.size());
for (Object o : objectList) {
newList.add(modifySingleObjectIfNeeded(o));
}
return newList;
} else if (obj.getClass().isArray()) {
int arraySize = Array.getLength(obj);
List newList = new ArrayList(arraySize);
for (int x = 0; x < arraySize; x++) {
newList.add(modifySingleObjectIfNeeded(Array.get(obj, x)));
}
return newList;
} else {
return modifySingleObjectIfNeeded(obj);
}
}
private Object modifySingleObjectIfNeeded(Object obj){
// let the JAXBIntrospector determine if the object is a JAXBElement
if (obj instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
return createXMLRootFromJAXBElement((JAXBElement) obj);
} else if(obj != null && obj.getClass().isEnum()) {
return wrapEnumeration(obj, obj.getClass());
} else if (obj instanceof JSONWithPadding){
Object nestedObject = ((JSONWithPadding)obj).getObject();
if(nestedObject != null){
Object newNestedObject = modifyObjectIfNeeded(nestedObject);
if(nestedObject != newNestedObject){
return new JSONWithPadding(newNestedObject,((JSONWithPadding)obj).getCallbackName());
}
}
}
return obj;
}
public void marshal(Object object, XMLStreamWriter streamWriter, TypeMappingInfo type) throws JAXBException {
if (jaxbContext.getTypeMappingInfoToGeneratedType() == null) {
marshal(object, streamWriter);
} else {
JAXBElement element = null;
Object value = object;
if (object instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
element = (JAXBElement) object;
value = element.getValue();
}
if(jaxbContext.getTypeMappingInfoToJavaTypeAdapters().size() > 0){
RootLevelXmlAdapter adapter = jaxbContext.getTypeMappingInfoToJavaTypeAdapters().get(type);
if (adapter != null) {
try {
value = adapter.getXmlAdapter().marshal(value);
} catch (Exception ex) {
throw new JAXBException(XMLMarshalException.marshalException(ex));
}
}
}
value = wrapObject(value, element, type);
marshal(value, streamWriter);
}
}
private Object wrapObject(Object object, JAXBElement wrapperElement, TypeMappingInfo typeMappingInfo) {
if(jaxbContext.getTypeMappingInfoToGeneratedType().size() > 0){
Class generatedClass = jaxbContext.getTypeMappingInfoToGeneratedType().get(typeMappingInfo);
if(generatedClass != null && object == null && wrapperElement != null) {
return wrapObjectInXMLRoot(wrapperElement, null, typeMappingInfo);
}
if (generatedClass != null && WrappedValue.class.isAssignableFrom(generatedClass)) {
ClassDescriptor desc = xmlMarshaller.getXMLContext().getSession(generatedClass).getDescriptor(generatedClass);
Object newObject = desc.getInstantiationPolicy().buildNewInstance();
((WrappedValue) newObject).setValue(object);
object = newObject;
} else if (generatedClass != null) {
// should be a many value
ClassDescriptor desc = xmlMarshaller.getXMLContext().getSession(generatedClass).getDescriptor(generatedClass);
Object newObject = desc.getInstantiationPolicy().buildNewInstance();
((ManyValue) newObject).setItem(object);
object = newObject;
}
}
if (null == wrapperElement) {
Root xmlRoot = new Root();
QName xmlTagName = typeMappingInfo.getXmlTagName();
if (null == xmlTagName) {
return object;
}
xmlRoot.setNamespaceURI(typeMappingInfo.getXmlTagName().getNamespaceURI());
xmlRoot.setLocalName(typeMappingInfo.getXmlTagName().getLocalPart());
xmlRoot.setObject(object);
return xmlRoot;
}
return wrapObjectInXMLRoot(wrapperElement, object, typeMappingInfo);
}
private Root wrapObjectInXMLRoot(JAXBElement wrapperElement, Object value, TypeMappingInfo typeMappingInfo) {
Root xmlroot = new Root();
Object objectValue = value;
xmlroot.setObject(objectValue);
QName qname = wrapperElement.getName();
xmlroot.setLocalName(qname.getLocalPart());
xmlroot.setNamespaceURI(qname.getNamespaceURI());
xmlroot.setDeclaredType(wrapperElement.getDeclaredType());
if(typeMappingInfo != null){
xmlroot.setSchemaType(typeMappingInfo.getSchemaType());
} else if(value != null) {
if (value.getClass() == CoreClassConstants.ABYTE || value.getClass() == CoreClassConstants.APBYTE ||
value.getClass().getCanonicalName().equals("jakarta.activation.DataHandler")) {
xmlroot.setSchemaType(Constants.BASE_64_BINARY_QNAME);
}
}
return xmlroot;
}
@Override
public void marshal(Object object, Writer writer) throws JAXBException {
if (object == null || writer == null) {
throw new IllegalArgumentException();
}
Listener listener = getListener();
if(listener != null) {
if(object instanceof JAXBElement) {
listener.beforeMarshal(object);
}
}
Object oxmObject = validateAndTransformIfNeeded(object); // xml bindings + object
try {
xmlMarshaller.marshal(oxmObject, writer);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception e) {
throw new MarshalException(e);
}
if(listener != null) {
if(object instanceof JAXBElement) {
listener.afterMarshal(object);
}
}
}
public void marshal(Object object, MarshalRecord record) throws JAXBException {
if (object == null || record == null) {
throw new IllegalArgumentException();
}
object = validateAndTransformIfNeeded(object); // xml bindings + object
try {
record.setMarshaller(xmlMarshaller);
xmlMarshaller.marshal(object, record);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception e) {
throw new MarshalException(e);
}
}
public void marshal(Object object, MarshalRecord record, TypeMappingInfo type) throws JAXBException {
if (jaxbContext.getTypeMappingInfoToGeneratedType() == null) {
marshal(object, record);
} else {
JAXBElement element = null;
Object value = object;
if (object instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
element = (JAXBElement) object;
value = element.getValue();
}
RootLevelXmlAdapter adapter = jaxbContext.getTypeMappingInfoToJavaTypeAdapters().get(type);
if (adapter != null) {
try {
value = adapter.getXmlAdapter().marshal(value);
} catch (Exception ex) {
throw new JAXBException(XMLMarshalException.marshalException(ex));
}
}
value = wrapObject(value, element, type);
marshal(value, record);
}
}
@Override
public void setAdapter(Class javaClass, XmlAdapter adapter) {
HashMap result = (HashMap) xmlMarshaller.getProperty(XML_JAVATYPE_ADAPTERS);
if (result == null) {
result = new HashMap();
xmlMarshaller.getProperties().put(XML_JAVATYPE_ADAPTERS, result);
}
result.put(javaClass, adapter);
}
@Override
public void setAdapter(XmlAdapter adapter) {
setAdapter(adapter.getClass(), adapter);
}
@Override
public void setAttachmentMarshaller(AttachmentMarshaller attachmentMarshaller) {
if (attachmentMarshaller == null) {
xmlMarshaller.setAttachmentMarshaller(null);
} else {
xmlMarshaller.setAttachmentMarshaller(new AttachmentMarshallerAdapter(attachmentMarshaller));
}
}
@Override
public void setEventHandler(ValidationEventHandler newValidationEventHandler) throws JAXBException {
if (null == newValidationEventHandler) {
validationEventHandler = JAXBContext.DEFAULT_VALIDATION_EVENT_HANDLER;
} else {
validationEventHandler = newValidationEventHandler;
}
xmlMarshaller.setErrorHandler(new JAXBErrorHandler(validationEventHandler));
}
@Override
public void setListener(Marshaller.Listener listener) {
if(xmlMarshaller.getMarshalListener() == null) {
xmlMarshaller.setMarshalListener(new JAXBMarshalListener(jaxbContext, this));
}
((JAXBMarshalListener) xmlMarshaller.getMarshalListener()).setListener(listener);
}
public void setMarshalCallbacks(Map callbacks) {
if(callbacks == null || callbacks.isEmpty()) {
return;
}
if(xmlMarshaller.getMarshalListener() == null) {
xmlMarshaller.setMarshalListener(new JAXBMarshalListener(jaxbContext, this));
}
((JAXBMarshalListener) xmlMarshaller.getMarshalListener()).setClassBasedMarshalEvents(callbacks);
}
/**
* Set a property on the JAXBMarshaller. Attempting to set any unsupported
* property will result in a jakarta.xml.bind.PropertyException
* @see org.eclipse.persistence.jaxb.MarshallerProperties
*/
@Override
public void setProperty(String key, Object value) throws PropertyException {
try {
if (key == null) {
throw new IllegalArgumentException();
} else {
SessionLog logger = AbstractSessionLog.getLog();
if (logger.shouldLog(SessionLog.FINE, SessionLog.MOXY)) {
logger.log(SessionLog.FINE, SessionLog.MOXY, "moxy_set_marshaller_property", new Object[] {key, value});
}
if (MOXySystemProperties.moxyLogPayload != null && xmlMarshaller.isLogPayload() == null) {
xmlMarshaller.setLogPayload(MOXySystemProperties.moxyLogPayload);
}
if (Constants.JAXB_FRAGMENT.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
Boolean fragment = (Boolean) value;
xmlMarshaller.setFragment(fragment.booleanValue());
} else if (JAXB_FORMATTED_OUTPUT.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
Boolean formattedOutput = (Boolean) value;
xmlMarshaller.setFormattedOutput(formattedOutput.booleanValue());
} else if (JAXB_ENCODING.equals(key)) {
xmlMarshaller.setEncoding((String) value);
} else if (JAXB_SCHEMA_LOCATION.equals(key)) {
xmlMarshaller.setSchemaLocation((String) value);
} else if (JAXB_NO_NAMESPACE_SCHEMA_LOCATION.equals(key)) {
xmlMarshaller.setNoNamespaceSchemaLocation((String) value);
} else if (MarshallerProperties.NAMESPACE_PREFIX_MAPPER.equals(key)) {
if (value == null) {
xmlMarshaller.setNamespacePrefixMapper(null);
} else if (value instanceof Map) {
NamespacePrefixMapper namespacePrefixMapper = new MapNamespacePrefixMapper((Map) value);
xmlMarshaller.setNamespacePrefixMapper(namespacePrefixMapper);
} else {
xmlMarshaller.setNamespacePrefixMapper((NamespacePrefixMapper) value);
}
} else if (SUN_NAMESPACE_PREFIX_MAPPER.equals(key) || SUN_JSE_NAMESPACE_PREFIX_MAPPER.equals(key)) {
if (value == null) {
xmlMarshaller.setNamespacePrefixMapper(null);
} else {
xmlMarshaller.setNamespacePrefixMapper(new NamespacePrefixMapperWrapper(value));
}
} else if (MarshallerProperties.INDENT_STRING.equals(key) || SUN_INDENT_STRING.equals(key) || SUN_JSE_INDENT_STRING.equals(key)) {
xmlMarshaller.setIndentString((String) value);
} else if (MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS.equals(key)) {
xmlMarshaller.setMarshalEmptyCollections((Boolean) value);
} else if (MarshallerProperties.JSON_REDUCE_ANY_ARRAYS.equals(key)) {
xmlMarshaller.setReduceAnyArrays((Boolean) value);
} else if (MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME.equals(key)) {
xmlMarshaller.setWrapperAsCollectionName((Boolean) value);
} else if (MarshallerProperties.JSON_USE_XSD_TYPES_WITH_PREFIX.equals(key)) {
xmlMarshaller.getJsonTypeConfiguration().setUseXsdTypesWithPrefix((Boolean) value);
} else if (MarshallerProperties.JSON_TYPE_COMPATIBILITY.equals(key)) {
xmlMarshaller.getJsonTypeConfiguration().setJsonTypeCompatibility((Boolean) value);
} else if (MarshallerProperties.JSON_TYPE_ATTRIBUTE_NAME.equals(key)) {
xmlMarshaller.getJsonTypeConfiguration().setJsonTypeAttributeName((String) value);
} else if (MarshallerProperties.JSON_DISABLE_NESTED_ARRAY_NAME.equals(key)) {
xmlMarshaller.getJsonTypeConfiguration().setJsonDisableNestedArrayName((Boolean) value);
} else if (MarshallerProperties.CHARACTER_ESCAPE_HANDLER.equals(key)) {
xmlMarshaller.setCharacterEscapeHandler((CharacterEscapeHandler) value);
} else if (MarshallerProperties.MOXY_LOG_PAYLOAD.equals(key)) {
xmlMarshaller.setLogPayload(((Boolean) value));
} else if (MarshallerProperties.MOXY_LOGGING_LEVEL.equals(key)) {
if (value instanceof String) {
AbstractSessionLog.getLog().setLevel(LogLevel.toValue((String) value).getId(), SessionLog.MOXY);
} else {
AbstractSessionLog.getLog().setLevel(((LogLevel) value).getId(), SessionLog.MOXY);
}
} else if (SUN_CHARACTER_ESCAPE_HANDLER.equals(key) || SUN_JSE_CHARACTER_ESCAPE_HANDLER.equals(key) || SUN_CHARACTER_ESCAPE_HANDLER_MARSHALLER.equals(key) || SUN_JSE_CHARACTER_ESCAPE_HANDLER_MARSHALLER.equals(key)) {
if (value == null) {
xmlMarshaller.setCharacterEscapeHandler(null);
} else {
xmlMarshaller.setCharacterEscapeHandler(new CharacterEscapeHandlerWrapper(value));
}
} else if (XML_DECLARATION.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
Boolean fragment = !(Boolean) value;
xmlMarshaller.setFragment(fragment.booleanValue());
} else if (XML_HEADERS.equals(key)) {
xmlMarshaller.setXmlHeader((String) value);
} else if (OBJECT_IDENTITY_CYCLE_DETECTION.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
xmlMarshaller.setEqualUsingIdenity(((Boolean) value).booleanValue());
} else if (MarshallerProperties.MEDIA_TYPE.equals(key)) {
MediaType mType = null;
if (value instanceof MediaType) {
mType = (MediaType) value;
} else if (value instanceof String) {
mType = MediaType.getMediaType((String) value);
}
if (mType == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
xmlMarshaller.setMediaType(mType);
} else if (MarshallerProperties.JSON_ATTRIBUTE_PREFIX.equals(key)) {
xmlMarshaller.setAttributePrefix((String) value);
} else if (MarshallerProperties.JSON_INCLUDE_ROOT.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
xmlMarshaller.setIncludeRoot((Boolean) value);
} else if (MarshallerProperties.JSON_VALUE_WRAPPER.equals(key)) {
if (value == null || (((String) value).length() == 0)) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
xmlMarshaller.setValueWrapper((String) value);
} else if (MarshallerProperties.JSON_NAMESPACE_SEPARATOR.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
xmlMarshaller.setNamespaceSeparator((Character) value);
} else if (MarshallerProperties.OBJECT_GRAPH.equals(key)) {
if (value == null) {
xmlMarshaller.setMarshalAttributeGroup(null);
} else if (value instanceof ObjectGraphImpl) {
xmlMarshaller.setMarshalAttributeGroup(((ObjectGraphImpl) value).getAttributeGroup());
} else if (value.getClass() == ClassConstants.STRING) {
xmlMarshaller.setMarshalAttributeGroup(value);
} else {
throw org.eclipse.persistence.exceptions.JAXBException.invalidValueForObjectGraph(value);
}
} else if (MarshallerProperties.BEAN_VALIDATION_MODE.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
this.beanValidationMode = ((BeanValidationMode) value);
} else if (MarshallerProperties.BEAN_VALIDATION_FACTORY.equals(key)) {
//noinspection StatementWithEmptyBody
if (value == null) {
// Allow null value for preferred validation factory.
}
this.prefValidatorFactory = value;
} else if (MarshallerProperties.BEAN_VALIDATION_GROUPS.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
this.beanValidationGroups = ((Class>[]) value);
} else if (MarshallerProperties.BEAN_VALIDATION_NO_OPTIMISATION.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
this.bvNoOptimisation = ((boolean) value);
} else {
throw new PropertyException(key, value);
}
}
} catch (ClassCastException exception) {
throw new PropertyException(key, exception);
}
}
@Override
public void setSchema(Schema schema) {
this.xmlMarshaller.setSchema(schema);
}
private Map getClassToGeneratedClasses() {
return jaxbContext.getClassToGeneratedClasses();
}
public JAXBContext getJaxbContext() {
return jaxbContext;
}
public XMLMarshaller getXMLMarshaller() {
return this.xmlMarshaller;
}
/**
* Returns constraint violations stored in the underlying
* {@link org.eclipse.persistence.jaxb.JAXBBeanValidator} instance.
*
* @return set of constraint violations from last unmarshal
*/
public Set> getConstraintViolations() {
if (beanValidator != null) {
return beanValidator.getConstraintViolations();
}
return Collections.emptySet();
}
private static class CharacterEscapeHandlerWrapper extends org.eclipse.persistence.internal.oxm.record.CharacterEscapeHandlerWrapper implements CharacterEscapeHandler {
public CharacterEscapeHandlerWrapper(Object sunHandler) {
super(sunHandler);
}
}
}