cxfUtil实现webservice客户端调用

cxfUtil代码实现类


package 

import gboat2.base.bridge.exception.DefaultGboatNestedException;
import .util.json.JsonUtil;
import .util.xml.JAXBUtil;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.common.jaxb.JAXBUtils;
import org.apache.cxf.common.jaxb.JAXBUtils.IdentifierType;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.service.model.BindingInfo;
import org.apache.cxf.service.model.BindingMessageInfo;
import org.apache.cxf.service.model.BindingOperationInfo;
import org.apache.cxf.service.model.MessagePartInfo;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;

import com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl;

/**
 * <p>
 * 使用 CXF 发布 WebService 服务和调用 WebService 服务的工具类。<br>
 * <small><b>注:</b>目前只提供了调用 WebService 服务相关的方法,而没提供发布服务相关的方法</small><br>
 * </p>
 * 该工具类解决了在 OSGI 环境中直接使用 CXF wsdl2java 生成的代码调用 WebService 时的以下问题:
 * <ol>
 * <li>传递复杂对象时,会抛出“<i>javax.xml.ws.WebServiceException: Could not find
 * wsdl:binding operation info for web method ...</i>”的异常</li>
 * <li>当 WebService 服务端的接口和实现类在不同的包下,而且实现类没指定 {@code @WebService} 注解的
 * {@code targetNamespace}
 * 属性时,会抛出“<i>org.apache.cxf.common.i18n.UncheckedException: No operation was
 * found with the name ...</i>”的异常。</li>
 * </ol>
 * 传递复杂参数(JavaBean)调用 WebService 服务的步骤:
 * <ol>
 * <li>使用 CXF 的 wsdl2java 工具生成 Java 代码;</li>
 * <li>使用刚才生成的 JavaBean 创建对象实例,并赋值;</li>
 * <li>调用本工具类的 {@link #invokeWebService(String, String, Object...)} 方法,将刚才创建的
 * JavaBean 实例作为参数传入;</li>
 * <li>如果返回结果中包含复杂对象(JavaBean),则可以通过 Spring 的 {@link BeanUtils} 或
 * commons-beanutils 的 {@link org.apache.commons.beanutils.BeanUtils BeanUtils}
 * 工具类将返回结果转换成刚才通过 wsdl2java 生成的 JavaBean,或是通过 json 工具进行转换(先将结果中的 JavaBean 转换成
 * json 字符串,再将 json 字符串转换成自己的 JavaBean),这样就可以更方便的读取返回结果中的具体信息了。</li>
 * </ol>
 */
@SuppressWarnings("restriction")
public class CXFUtil {

    private final static Logger logger = LoggerFactory.getLogger(CXFUtil.class);
    private final static int CONFIGURE_CLIENT_SIZE = 999999999;

    static {
        // 设置 XPathFactory,解决调用 webService 传递复杂对象时抛出 java.lang.ExceptionInInitializerError 错误的问题
        System.setProperty(XPathFactory.DEFAULT_PROPERTY_NAME + ":" + XPathFactory.DEFAULT_OBJECT_MODEL_URI,
                XPathFactoryImpl.class.getName());
    }
    
    /**
     * 将构造方法设为私有,防止在外部被实例化
     */
    private CXFUtil() {
    }
    
    /**
     * 动态创建 WebService 的 JAX-WS 方式调用客户端实例<br>
     * 和直接执行 {@code createJaxWsDynamicClient(wsdlUrl, JaxWsDynamicClientFactory.class.getClassLoader())} 返回的结果一样
     * @param wsdlUrl WSDL 地址
     * @return
     */
    public static Client createJaxWsDynamicClient(String wsdlUrl){
        return createJaxWsDynamicClient(wsdlUrl, JaxWsDynamicClientFactory.class.getClassLoader());
    }
    
    /**
     * 动态创建 WebService 的 JAX-WS 方式调用客户端实例<br>
     * 和直接执行 {@code createJaxWsDynamicClient(wsdlUrl, JaxWsDynamicClientFactory.class.getClassLoader())} 返回的结果一样
     * @param wsdlUrl WSDL 地址
     * @param authorityParameter 调用服务端校验,用户名和密码
     * @return
     */
    public static Client createJaxWsDynamicClient(String wsdlUrl, AuthorityParameter authorityParameter){
        return createJaxWsDynamicClient(wsdlUrl, authorityParameter, null);
    }
    
    /**
     * 动态创建 WebService 的 JAX-WS 方式调用客户端实例
     * @param wsdlUrl WSDL 地址
     * @param classLoader 类加载器,调用 CXF 的
     *            {@link org.apache.cxf.endpoint.dynamic.DynamicClientFactory#createClient(String, ClassLoader)
     *            DynamicClientFactory#createClient(String, ClassLoader)} 方法动态创建客户端时,将使用此类加载器
     * @return
     */
    public static Client createJaxWsDynamicClient(String wsdlUrl, ClassLoader classLoader) {
        return createJaxWsDynamicClient(wsdlUrl, null, classLoader);
    }
    
    /**
     * 动态创建 WebService 的 JAX-WS 方式调用客户端实例
     * @param wsdlUrl WSDL 地址
     * @param authorityParameter 调用服务端校验,用户名和密码
     * @param classLoader 类加载器,调用 CXF 的
     *            {@link org.apache.cxf.endpoint.dynamic.DynamicClientFactory#createClient(String, ClassLoader)
     *            DynamicClientFactory#createClient(String, ClassLoader)} 方法动态创建客户端时,将使用此类加载器
     * @return
     */
    public static Client createJaxWsDynamicClient(String wsdlUrl, AuthorityParameter authorityParameter, ClassLoader classLoader) {
        // 创建 HttpClient 策略
        HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
        httpClientPolicy.setAllowChunking(false);
        httpClientPolicy.setChunkingThreshold(CONFIGURE_CLIENT_SIZE);

        // 动态创建 WebService 客户端
        Client client = GboatJaxWsDynamicClientFactory.newInstance().createClient(wsdlUrl, classLoader);
        // 设置用户名密码
        if(null != authorityParameter) {
        	client.getOutInterceptors().add(new AuthorityHeaderInterceptor(authorityParameter)); 
        	client.getOutInterceptors().add(new LoggingOutInterceptor());          	
        }
        
        ((HTTPConduit) client.getConduit()).setClient(httpClientPolicy);
        return client;
    }
    
    /**
     * 动态创建 WebService 的 JAX-WS 方式调用客户端实例
     * @param wsdlUrl WSDL 地址
     * @param interceptor 自定义用户名和密码拦截器
     * @param classLoader 类加载器,调用 CXF 的
     *            {@link org.apache.cxf.endpoint.dynamic.DynamicClientFactory#createClient(String, ClassLoader)
     *            DynamicClientFactory#createClient(String, ClassLoader)} 方法动态创建客户端时,将使用此类加载器
     * @return
     */
    public static Client createJaxWsDynamicClient(String wsdlUrl, AbstractPhaseInterceptor<SoapMessage> interceptor) {
        // 创建 HttpClient 策略
        HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
        httpClientPolicy.setAllowChunking(false);
        httpClientPolicy.setChunkingThreshold(CONFIGURE_CLIENT_SIZE);

        // 动态创建 WebService 客户端
        Client client = GboatJaxWsDynamicClientFactory.newInstance().createClient(wsdlUrl);
        // 设置用户名密码
        if(null != interceptor) {
        	client.getOutInterceptors().add(interceptor); 
        	client.getOutInterceptors().add(new LoggingOutInterceptor());          	
        }
        
        ((HTTPConduit) client.getConduit()).setClient(httpClientPolicy);
        return client;
    }
    /**
     * 获取 CXF 的绑定操作信息的实例
     * @param client CXF 客户端,可通过 {@link #createJaxWsDynamicClient(String)} 进行创建
     * @param operation 操作名称(一般和方法名相同),如: sayHello
     * @return 绑定操作信息的实例。如果没有找到对应的绑定操作,则返回 <code>null</code>
     */
    public static BindingOperationInfo getBindingOperationInfo(Client client, String operation){
        Endpoint endpoint = client.getEndpoint();  
        BindingInfo bindingInfo = endpoint.getEndpointInfo().getBinding();  
        QName opName = new QName(endpoint.getService().getName().getNamespaceURI(), operation);  
        BindingOperationInfo boi = bindingInfo.getOperation(opName);
        if (boi == null) { // WebService 接口和实现类的 namespace 不相同的情况
            for (BindingOperationInfo operationInfo : bindingInfo.getOperations()) {
                if (operation.equals(operationInfo.getName().getLocalPart())) {
                    return operationInfo;
                }
            }
        }
        return boi;
    }

    /**
     * 获取操作对象的输入参数
     * @param boi 操作对象
     * @return 操作的输入参数 Map,key 为参数的名称,value 为参数的类型
     */
    public static Map<String, Class<?>> getInputParameters(BindingOperationInfo operationInfo) {
        return getParameters(operationInfo, true);
    }
    
    /**
     * 获取操作对象的输出参数
     * @param boi 操作对象
     * @return 操作的输出参数 Map,key 为参数的名称,value 为参数的类型
     */
    public static Map<String, Class<?>> getOutputParameters(BindingOperationInfo operationInfo) {
        return getParameters(operationInfo, false);
    }
    
    /**
     * 获取操作对象的输入或输出参数
     * @param boi 操作对象
     * @param input true 表示获取输入参数,false 表示获取输出参数
     * @return 操作的指定类型参数 Map,key 为参数的名称,value 为参数的类型
     */
    private static Map<String, Class<?>> getParameters(BindingOperationInfo boi, boolean input) {
       	BindingOperationInfo unWrappedBoi = boi.isUnwrapped() ? boi.getUnwrappedOperation() : boi.getWrappedOperation();
       	if(unWrappedBoi == null) { // 在服务端使用@SOAPBinding(style = SOAPBinding.Style.RPC),即设置绑定方式为RPC时,这个返回为空
       		unWrappedBoi = boi;
       	}
        BindingMessageInfo bmi = (input ? unWrappedBoi.getInput() : unWrappedBoi.getOutput());
        List<MessagePartInfo> messageParts = bmi.getMessageParts();
        Map<String, Class<?>> result = new LinkedHashMap<String, Class<?>>(messageParts.size());
        for (MessagePartInfo mpi : messageParts) {
            result.put(mpi.getName().getLocalPart(), mpi.getTypeClass());
        }
        return result;
    }
    
    /**
     * 调用 webservice 服务<br>
     * 和直接执行
     * {@code invokeWebService(wsdlUrl, operation, JaxWsDynamicClientFactory.class.getClassLoader(), parameters)}
     * 返回的结果一样
     * @param wsdlUrl WSDL 地址
     * @param operation 要调用的操作(WebService 服务名称)
     * @param parameters 参数列表,如果没有参数,则可以不传入该参数值
     * @return WebService 服务端返回的结果
     * @see #invokeWebService(String, String, ClassLoader, Object...)
     */
    public static Object[] invokeWebService(String wsdlUrl, String operation, Object... parameters) {
        return invokeWebService(wsdlUrl, operation, JaxWsDynamicClientFactory.class.getClassLoader(), parameters);
    }

    /**
     * 调用 webservice 服务
     * 
     * @param wsdlUrl WSDL 地址
     * @param operation 要调用的操作(WebService 服务名称)
     * @param classLoader 类加载器,调用 CXF 的
     *            {@link org.apache.cxf.endpoint.dynamic.DynamicClientFactory#createClient(String, ClassLoader)
     *            DynamicClientFactory#createClient(String, ClassLoader)} 方法动态创建客户端时,将使用此类加载器
     * @param parameters 参数列表,如果没有参数,则可以不传入该参数值
     * @return WebService 服务端返回的结果
     */
    public static Object[] invokeWebService(String wsdlUrl, String operation,
            ClassLoader classLoader, Object... parameters) {
        // 动态创建 WebService 客户端
        Client client = createJaxWsDynamicClient(wsdlUrl, classLoader);
        // 调用 WebService 服务
        return invokeWebService(client, operation, parameters);
    }
    
    /**
     * 调用 webservice 服务
     * @param client WebService 客户端实例对象
     * @param operation 要调用的操作(WebService 服务名称)
     * @param parameters 参数列表,如果没有参数,则可以不传入该参数值
     * @return 服务端返回的结果
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static Object[] invokeWebService(Client client, String operation, Object... parameters) {
    	Object[] invokeParameters = new Object[parameters.length];
        try {
            BindingOperationInfo boi = getBindingOperationInfo(client, operation);
            if(boi == null)
                throw new DefaultGboatNestedException(
                        "No operation was found with the name [" + operation + "] of namespace "
                                + client.getEndpoint().getService().getName().getNamespaceURI());
           
            
            BindingOperationInfo invokeBoi = null;
            if(boi.isUnwrapped()) {
            	invokeBoi = boi.getUnwrappedOperation();
            } else {
            	invokeBoi = boi.getWrappedOperation();
            }
            
            // 在服务端使用@SOAPBinding(style = SOAPBinding.Style.RPC),上边的方式拿到的invokeBoi是空的,因此做以下处理	
            if(null == invokeBoi) { 
            	invokeBoi = boi;
            }
            
            // WebService 服务需要的输入参数的信息
            Map<String,Class<?>> inputParamInfoMap = getInputParameters(boi);
            
            if(ArrayUtils.isNotEmpty(parameters) && MapUtils.isNotEmpty(inputParamInfoMap)) {
                List<Class<?>> needTypes = new ArrayList<Class<?>>(inputParamInfoMap.values());
                Class<?> needType = null; // 需要的类型
                Class<?> actualType = null; // 实际传入的类型
                for (int i = 0, size = needTypes.size(); i < size; i++) {
                	
                    // 如果实际传入的参数个数小于需要的参数个数,且实际传入的参数已经全部被处理完成,则中止循环
                    if(i >= parameters.length)
                        break;
                    
                    // 如果当前参数值为 null,则什么也不做,直接跳过
                    if(parameters[i] == null) {
                    	invokeParameters[i] = parameters[i];
                        continue;
                    }
                    
                    // 如果传入参数值是java基本类型也不做处理,直接赋值
                    if(isNativeType(parameters[i].getClass())) {
                    	invokeParameters[i] = parameters[i];
                    	continue;
                    }
                    needType = needTypes.get(i);
                    actualType = parameters[i].getClass();
                    
                    // 先进行参数类型校验
                    parametersValidate(parameters[i].getClass(), needType);
                    
                    // 如果当前参数值可以强转为需要的参数类型或为原生类型,则什么也不做,直接跳过
                    if(needType == actualType || needType.isAssignableFrom(actualType) || ClassUtils.isPrimitiveOrWrapper(actualType)) {
                        continue;
                    }
                    
                    // 使用需要的类型创建一个新的对象实例,并将当前参数的属性值复制给新创建的实例
                    Object obj = null;
                    try {
                        if (JAXBElement.class.isAssignableFrom(needType)
                                || needType.isAnnotationPresent(XmlRootElement.class)
                                || needType.isAnnotationPresent(XmlType.class)) { // 单个复杂对象处理
                            obj = JAXBUtil.castClone(parameters[i], needType);
                        } else if (Collection.class.isAssignableFrom(parameters[i].getClass())) { // List处理,如果根据needType判断,则即使服务端是List,needType的isArray也是true
                        	// 取到原始的数组中的原子对象类型
                            Class<?> needComponentType = needType.getComponentType();
                            Collection collection = ArrayList.class.newInstance();
                        	// 防止服务端传入的参数是实际的列表类,而不是接口
                        	if(parameters[i] instanceof LinkedList) {
                        		collection = LinkedList.class.newInstance();
                        	}
                        	List<?> listInputParameters = (List)parameters[i];                        	
                        	for(int j = 0; j < listInputParameters.size(); j++) {
                        		Object tempDestObj = JAXBUtil.castClone(listInputParameters.get(j), needComponentType);
                        		collection.add(tempDestObj);
                        	}
                        	obj = collection;
                        } else if(parameters[i].getClass().isArray() || needType.isArray()) { // 数组处理
                        	// 数组类型的参数处理,判断数组时,不能用parameters[i]的方式,而应该全部获取到
                        	Class<?> needComponentType = needType.getComponentType();
                        	Object[] inArray = (Object[]) parameters[i];
                        	// 即使服务端发布的服务参数是数组,也必须使用List对象传给参数,原因暂时没有搞清楚
                            Collection collection = ArrayList.class.newInstance();
                        	// 防止服务端传入的参数是实际的列表类,而不是接口
                        	if(parameters[i] instanceof LinkedList) {
                        		collection = LinkedList.class.newInstance();
                        	}                     	
                        	for(int j = 0; j < inArray.length; j++) {
                        		Object tempDestObj = JAXBUtil.castClone(inArray[j], needComponentType);
                        		collection.add(tempDestObj);
                        	}
                        	obj = collection;
                        } else {
                            obj = needType.newInstance();
                            BeanUtils.copyProperties(parameters[i], obj);
                        }
                    } catch (Exception e) {
                        logger.warn(
                                "通过 CXF 调用 WebService [{}] 时,将第 [{}] 个参数 [{}] 的属性值复制给目标类型的对象 [{}] 失败,将改用 JSON 转换的方式将其转换为目标类型。",
                                boi, i, actualType, needType);
                        logger.warn("异常信息:", e);
                        // 如果直接复制失败,则先将源对象转换成 Json 字符串,然后再将 Json 字符串转换成目标对象
                        String json = JsonUtil.object2Json(parameters[i]);
                        obj = JsonUtil.json2Object(json, needType);
                    }
                    invokeParameters[i] = obj;
                }
            }
            
            // 调用 WebService 服务
            return client.invoke(invokeBoi, invokeParameters);
        } catch (Exception e) {
            throw new DefaultGboatNestedException("使用 CXF 动态调用 WebService 服务 [" + operation + "] 失败", e);
        }
    }

    /**
     * 检查请求参数
     * <p>暂时不支持Map类型,Map类型需要用一个类来包装!
     * @param parameter
     */
    private static void parametersValidate(Class<?> parameter, Class<?> destType) {
    	if(Map.class.isAssignableFrom(parameter)) {
    		throw new RuntimeException("暂时不支持Map类型,你可以将该类型作为一个类的成员变量这样的方式来完成调用。");
    	}
    	
    	if(JAXBElement.class.isAssignableFrom(destType)) {
    		throw new RuntimeException("参数转换失败,请在webservice的服务端接口中标注@WebService注解!");
    	}
    }
    
    /**
     * 调用 WebService 服务
     * 
     * @param wsdlUrl WSDL 地址
     * @param parameter 经过包装后的请求参数,与 WSDL 文件中 &lt;wsdl:operation name="xxx"&gt;
     *            相对应的 JavaBean,该类必须有 &#64;XmlRootElement 注解,&#64;XmlRootElement
     *            注解的 name 属性对应 WSDL 文件中 operation 的 name
     * @return WebService 服务端的返回结果
     */
    public static Object[] invokeWrappedWebService(String wsdlUrl, Object parameter){
        return invokeWrappedWebService(wsdlUrl, parameter, JaxWsDynamicClientFactory.class.getClassLoader());
    }
    
    /**调用 WebService 服务
     * 
     * @param wsdlUrl WSDL 地址
     * @param parameter 经过包装后的请求参数,与 WSDL 文件中 &lt;wsdl:operation name="xxx"&gt;
     *            相对应的 JavaBean,该类必须有 &#64;XmlRootElement 注解,&#64;XmlRootElement
     *            注解的 name 属性对应 WSDL 文件中 operation 的 name
     * @param classLoader 类加载器,调用 CXF 的
     *            {@link org.apache.cxf.endpoint.dynamic.DynamicClientFactory#createClient(String, ClassLoader)
     *            DynamicClientFactory#createClient(String, ClassLoader)} 方法动态创建客户端时,将使用此类加载器
     * @return WebService 服务端的返回结果
     */
    public static Object[] invokeWrappedWebService(String wsdlUrl, Object parameter, ClassLoader classLoader){
        // 动态创建 WebService 客户端
        Client client = createJaxWsDynamicClient(wsdlUrl, classLoader);
        // 调用 WebService 服务
        return invokeWrappedWebService(client, parameter);
    }
    
    /**
     * 调用 WebService 服务
     * 
     * @param client WebService 客户端实例对象
     * @param parameter 经过包装后的请求参数,与 WSDL 文件中 &lt;wsdl:operation name="xxx"&gt;
     *            相对应的 JavaBean,该类必须有 &#64;XmlRootElement 注解,&#64;XmlRootElement
     *            注解的 name 属性对应 WSDL 文件中 operation 的 name
     * @return WebService 服务端的返回结果
     */
    public static Object[] invokeWrappedWebService(Client client, Object parameter) {
        Assert.notNull(parameter, "使用经过 CXF 包装后客户端调用 WebService 失败,原因:传入的参数不能为 null");
        
        Class<?> paramType = parameter.getClass();
        XmlRootElement xmlRootElement = paramType.getAnnotation(XmlRootElement.class);
        Assert.notNull(xmlRootElement, "使用经过 CXF 包装后客户端调用 WebService 失败,原因:传入的参数类型 [" + paramType + "] 必须有 javax.xml.bind.annotation.XmlRootElement 注解");
        
        String operation = xmlRootElement.name();
        if("##default".equals(operation)) {
            operation = paramType.getSimpleName();
        }
        
        String reallyOperation = StringUtils.removeEnd(WordUtils.uncapitalize(operation), "Request");
        try {
            BindingOperationInfo boi = getBindingOperationInfo(client, reallyOperation);
            if(boi == null)
                throw new DefaultGboatNestedException(
                        "No operation was found with the name [" + reallyOperation + "] of namespace " + client.getEndpoint().getService().getName().getNamespaceURI());

            QName qname = boi.getName();
            String packageName = JAXBUtils.namespaceURIToPackage(qname.getNamespaceURI());
            String simpleClassName = JAXBUtils.nameToIdentifier(operation, IdentifierType.CLASS);
            String className = packageName + "." + simpleClassName;

            // CXF 动态生成的客户端方法需要的参数类型
            Class<?> needType = Thread.currentThread().getContextClassLoader().loadClass(className);
            if(!needType.isAssignableFrom(paramType)){
                // 将当前传入的参数转换成与需要的类型相匹配
                parameter = JAXBUtil.castClone(parameter, needType);
            }
            
            // 调用 WebService 服务
            return client.invoke(boi, parameter);
        } catch (Exception e) {
            throw new DefaultGboatNestedException("使用经过 CXF 包装后客户端调用 WebService [" + reallyOperation + "] 失败", e);
        }
    }
    
    /**
     * 判断传入的参数类型是不是java自带的基本类型
     * @param clazz
     * @return
     */
    public static boolean isNativeType(Class<?> type) {
    	if (type.isPrimitive()) { // 基本类型
			return true;
		}

		// 不可更改的变量类型 如 String,Long
		if (type.equals(String.class))
			return true;
		if (type.equals(Long.class))
			return true;
		if(type.equals(Boolean.class))
			return true;
		if(type.equals(Short.class))
			return true;
		if(type.equals(Integer.class))
			return true;
		if(type.equals(Character.class))
			return true;			 
		if(type.equals(Float.class))
			return true;			 
		if(type.equals(Double.class))
			return true;
		if(type.equals(Byte.class))
			return true;

		return false;
    }
}

jsonUtil代码

package ;

import ;
import .util.DateUtil;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.math.BigDecimal;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapLikeType;
import com.fasterxml.jackson.databind.type.TypeFactory;


@SuppressWarnings("unchecked")
public final class JsonUtil {

    private static ObjectMapper MAPPER;
    
    static {
        MAPPER = generateMapper();
    }

    /**
     * 创建 ObjectMapper 对象
     * 
     * @return 新创建的 ObjectMapper 实例
     */
    public static ObjectMapper generateMapper() {
        return generateMapper(Include.ALWAYS);
    }

    /**
     * 创建 ObjectMapper 对象
     * 
     * @param incl 传入一个枚举值,设置序列化为 json 的 JavaBean 属性的类型:
     *            <ul>
     *            <li>{@link Include#ALWAYS} - 全部列入</li>
     *            <li>{@link Include#NON_DEFAULT} - 字段值和其默认值相同的时候不会列入</li>
     *            <li>{@link Include#NON_EMPTY} - 字段值为 null、"" 或 length、size 为 0 的时候不会列入</li>
     *            <li>{@link Include#NON_NULL} - 字段值为 null 时候不会列入</li>
     *            </ul>
     * @return 新创建的 ObjectMapper 实例
     */
    public static ObjectMapper generateMapper(Include incl) {
        return generateMapper(incl, DateUtil.DEFAULT_DATETIME_FORMAT);
    }

    /**
     * 创建 ObjectMapper 对象
     * 
     * @param incl 传入一个枚举值,设置序列化为 json 的 JavaBean 属性的类型:
     *            <ul>
     *            <li>{@link Include#ALWAYS} - 全部列入</li>
     *            <li>{@link Include#NON_DEFAULT} - 字段值和其默认值相同的时候不会列入</li>
     *            <li>{@link Include#NON_EMPTY} - 字段值为 null、"" 或 length、size 为 0 的时候不会列入</li>
     *            <li>{@link Include#NON_NULL} - 字段值为 null 时候不会列入</li>
     *            </ul>
     * @param dateFormat 日期字符串的格式
     * @return 新创建的 ObjectMapper 实例
     */
    public static ObjectMapper generateMapper(Include incl, String dateFormat) {
        /*
        JsonFactory jsonFactory = new JsonFactory();
        jsonFactory.enable(Feature.ALLOW_COMMENTS);
        jsonFactory.enable(Feature.ALLOW_SINGLE_QUOTES);
        jsonFactory.enable(Feature.ALLOW_UNQUOTED_FIELD_NAMES);
        ObjectMapper objectMapper = new ObjectMapper(jsonFactory);
        */
        
        ObjectMapper objectMapper = initObjectMapper(incl, dateFormat);

        SimpleModule module = new SimpleModule("GboatModule");
        module.addSerializer(Enum.class, new GboatEnumSerializer())
                .addSerializer(BigDecimal.class, new GboatBigDecimalSerializer())
                .addDeserializer(Boolean.class, new GboatBooleanDeserializer())
                .addDeserializer(Date.class, new GboatDateDeserializer());
        objectMapper.registerModule(module);

        return objectMapper;
    }
    
    /**
     * 序列化的時候用原生的枚举序列化方式
     * @return
     */
    public static ObjectMapper generateMapperDefaultEnum(Include incl, String dateFormat) {
        
        ObjectMapper objectMapper = initObjectMapper(incl, dateFormat);

        SimpleModule module = new SimpleModule("GboatModule");
        module.addSerializer(BigDecimal.class, new GboatBigDecimalSerializer())
                .addDeserializer(Boolean.class, new GboatBooleanDeserializer())
                .addDeserializer(Date.class, new GboatDateDeserializer());
        objectMapper.registerModule(module);

        return objectMapper;
    }

	/**
	 * @param incl
	 * @param dateFormat
	 * @return
	 */
	private static ObjectMapper initObjectMapper(Include incl, String dateFormat) {
		ObjectMapper objectMapper = new ObjectMapper();

        // 设置输入时忽略在 JSON 字符串中存在但 Java 对象实际没有的属性
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 对于原生数据类型的属性允许 JSON 字符串中的值为 null
        objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
        // 禁止使用 int 代表 Enum 的 order() 来反序列化 Enum,非常危险
        objectMapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
        //允许数组为单值
        objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.configure(SerializationFeature.INDENT_OUTPUT, false);
        objectMapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN, true);

        // 设置输出时包含属性的风格
        objectMapper.setSerializationInclusion(incl);
        // 设置默认日期格式
        objectMapper.setDateFormat(new SimpleDateFormat(dateFormat));
        // objectMapper.setFilters(new SimpleFilterProvider().setFailOnUnknownId(false));
		return objectMapper;
	}

    /**
     * 将 JSON 字符串转换成 Java 对象
     * 
     * @param json json 字符串
     * @param clazz 要转换的目标类型
     * @param <T> JSON 字符串需要转换的目标类型
     * @return 转换后的 Java 对象
     */
    public static <T> T json2Object(String json, Class<T> clazz) {
        if (String.class.equals(clazz)) {
            return (T) json;
        }
        try {
            return (T) MAPPER.readValue(json, clazz);
        } catch (Exception e) {
            throw new DefaultGboatNestedException("将 " + json + " 转换成 " + clazz + " 对象失败。", e);
        }
    }

    /**
     * 将 JSON 字符串转换成 Java 对象
     * 
     * @param json JSON 字符串
     * @param typeReference 类型引用对象
     * @param <T> JSON 字符串需要转换的目标类型
     * @return 转换后的 Java 对象
     */
    public static <T> T json2Object(String json, TypeReference<T> typeReference) {
        if (String.class.equals(typeReference.getType())) {
            return (T) json;
        }

        try {
            return (T) MAPPER.readValue(json, typeReference);
        } catch (Exception e) {
            throw new DefaultGboatNestedException("将 " + json + " 转换成 " + typeReference.getType() + " 对象失败。", e);
        }
    }

    /**
     * 将 JSON 字符串转换成 List
     * 
     * @param json JSON 字符串
     * @param elementClass List 中元素的类型
     * @param <T> JSON 字符串需要转换的目标类型
     * @return 转换后得到的 List 实例
     */
    public static <T> List<T> json2List(String json, Class<T> elementClass) {
        CollectionType typeReference = TypeFactory.defaultInstance()
                .constructCollectionType(ArrayList.class, elementClass);
        try {
            return (List<T>) MAPPER.readValue(json, typeReference);
        } catch (Exception e) {
            throw new DefaultGboatNestedException("将 " + json + " 转换成 List<" + elementClass + "> 对象失败。", e);
        }
    }

    /**
     * 将 JSON 字符串转换成 List
     * 
     * @param json JSON 字符串
     * @param keyClass Map 中 key 的类型
     * @param valueClass Map 中 value 的类型
     * @param <K> the type of keys maintained by this map
     * @param <V> the type of mapped values
     * @return 转换后得到的 Map 实例
     */
    public static <K, V> Map<K, V> json2Map(String json, Class<K> keyClass,
            Class<V> valueClass) {
        MapLikeType typeReference = TypeFactory
                .defaultInstance()
                .constructMapLikeType(LinkedHashMap.class, keyClass, valueClass);
        try {
            return (Map<K, V>) MAPPER.readValue(json, typeReference);
        } catch (Exception e) {
            throw new DefaultGboatNestedException("将 " + json + " 转换成 Map<" + keyClass + ", " + valueClass + "> 对象失败。", e);
        }
    }
    
    /**
     * 将 Java 对象转换成 JSON 字符串
     * 
     * @param obj 要转换成 JSON 字符串的 Java 对象,可以是一个 JavaBea、数组、List、Map 等任何非 null 对象
     * @return 转换后的 JSON 字符串
     */
    public static String object2Json(Object obj) {
        try {
            return MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new DefaultGboatNestedException("将 " + obj + " 转换成 JSON 字符串失败。", e);
        }
    }
    
    /**
     * 将 Java 对象转换成 JSON 字符串
     * 
     * @param obj 要转换成 JSON 字符串的 Java 对象。该 Java 对象的类定义必须使用 &#64;
     *            {@link JsonFilter} 注解进行标注
     * @param properties 要序列化成 JSON 字符串的属性
     * @return 转换后的 JSON 字符串
     * @throws IllegalArgumentException - 传入的 obj 对象的类定义没有使用 &#64;
     *             {@link JsonFilter} 注解进行标注时,抛出此异常
     */
    public static String object2Json(Object obj, String[] properties) {
        try {
            return getIncludeObjectWriter(obj.getClass(), properties).writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new DefaultGboatNestedException("将 " + obj + " 转换成 JSON 字符串失败。", e);
        }
    }
    
    /**
     * 将 Java 对象转换成 JSON 字符串
     * 
     * @param obj 要转换成 JSON 字符串的 Java 对象。如果 excludeProperties 不为 null 且长度大于 0, 该
     *            Java 对象的类定义必须使用 &#64;{@link JsonFilter} 注解进行标注
     * @param excludeProperties 将 Java 对象序列化成 JSON 字符串时需要排除的属性
     * @return 转换后的 JSON 字符串
     * @throws IllegalArgumentException - excludeProperties 不为空,且传入的 obj
     *             对象的类定义没有使用 &#64;{@link JsonFilter} 注解进行标注时,抛出此异常
     */
    public static String object2JsonWithExcludeProps(Object obj, String[] excludeProperties) {
        try {
            return getExcludeObjectWriter(obj.getClass(), excludeProperties).writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new DefaultGboatNestedException("将 " + obj + " 转换成 JSON 字符串失败。", e);
        }
    }
    
    /**
     * 将 Java 对象转换成 JSON 字符串,并输出到指定的输出流中
     * 
     * @param out 目标输出流
     * @param obj 要转换成 JSON 字符串的 Java 对象,可以是一个 JavaBea、数组、List、Map 等任何非 null 对象
     */
    public static void object2Json(OutputStream out, Object obj) {
        try {
            MAPPER.writeValue(out, obj);
        } catch (Exception e) {
            throw new DefaultGboatNestedException(e);
        }
    }
    
    /**
     * 将 Java 对象转换成 JSON 字符串,并输出到指定的输出流中
     * 
     * @param out 目标输出流
     * @param obj 要转换成 JSON 字符串的 Java 对象。该 Java 对象的类定义必须使用 &#64;
     *            {@link JsonFilter} 注解进行标注
     * @param properties 要序列化成 JSON 字符串的属性
     * @throws IllegalArgumentException - 传入的 obj 对象的类定义没有使用 &#64;
     *             {@link JsonFilter} 注解进行标注时,抛出此异常
     */
    public static void object2Json(OutputStream out, Object obj, String[] properties) {
        try {
            getIncludeObjectWriter(obj.getClass(), properties).writeValue(out, obj);
        } catch (Exception e) {
            throw new DefaultGboatNestedException(e);
        }
    }
    
    /**
     * 将 Java 对象转换成 JSON 字符串,并输出到指定的输出流中
     * 
     * @param out 目标输出流
     * @param obj 要转换成 JSON 字符串的 Java 对象。如果 excludeProperties 不为 null 且长度大于 0, 该
     *            Java 对象的类定义必须使用 &#64;{@link JsonFilter} 注解进行标注
     * @param excludeProperties 将 Java 对象序列化成 JSON 字符串时需要排除的属性
     * @throws IllegalArgumentException - excludeProperties 不为空,且传入的 obj
     *             对象的类定义没有使用 &#64;{@link JsonFilter} 注解进行标注时,抛出此异常
     */
    public static void object2JsonWithExcludeProps(OutputStream out, Object obj, String[] excludeProperties) {
        try {
            getExcludeObjectWriter(obj.getClass(), excludeProperties).writeValue(out, obj);
        } catch (Exception e) {
            throw new DefaultGboatNestedException(e);
        }
    }
    
    /**
     * 将 Java 对象转换成 JSON 字符串,并输出到指定的输出流中
     * 
     * @param write 目标输出流
     * @param obj 要转换成 JSON 字符串的 Java 对象,可以是一个 JavaBea、数组、List、Map 等任何非 null 对象
     */
    public static void object2Json(Writer write, Object obj) {
        try {
            MAPPER.writeValue(write, obj);
        } catch (Exception e) {
            throw new DefaultGboatNestedException(e);
        }
    }
    
    /**
     * 将 Java 对象转换成 JSON 字符串,并输出到指定的输出流中
     * 
     * @param write 目标输出流
     * @param obj 要转换成 JSON 字符串的 Java 对象。该 Java 对象的类定义必须使用 &#64;
     *            {@link JsonFilter} 注解进行标注
     * @param properties 要序列化成 JSON 字符串的属性
     * @throws IllegalArgumentException - 传入的 obj 对象的类定义没有使用 &#64;
     *             {@link JsonFilter} 注解进行标注时,抛出此异常
     */
    public static void object2Json(Writer write, Object obj, String[] properties) {
        try {
            getIncludeObjectWriter(obj.getClass(), properties).writeValue(write, obj);
        } catch (Exception e) {
            throw new DefaultGboatNestedException(e);
        }
    }
    
    /**
     * 将 Java 对象转换成 JSON 字符串,并输出到指定的输出流中
     * 
     * @param write 目标输出流
     * @param obj 要转换成 JSON 字符串的 Java 对象。如果 excludeProperties 不为 null 且长度大于 0, 该
     *            Java 对象的类定义必须使用 &#64;{@link JsonFilter} 注解进行标注
     * @param excludeProperties 将 Java 对象序列化成 JSON 字符串时需要排除的属性
     * @throws IllegalArgumentException - excludeProperties 不为空,且传入的 obj
     *             对象的类定义没有使用 &#64;{@link JsonFilter} 注解进行标注时,抛出此异常
     */
    public static void object2JsonWithExcludeProps(Writer write, Object obj, String[] excludeProperties) {
        try {
            getExcludeObjectWriter(obj.getClass(), excludeProperties).writeValue(write, obj);
        } catch (Exception e) {
            throw new DefaultGboatNestedException(e);
        }
    }
    
    /**
     * 将 Java 对象转换成 JSON 字符串,并保存到指定的文件中
     * 
     * @param file 目标输出流
     * @param obj 要转换成 JSON 字符串的 Java 对象,可以是一个 JavaBea、数组、List、Map 等任何非 null 对象
     */
    public static void object2Json(File file, Object obj) {
        try {
            MAPPER.writeValue(file, obj);
        } catch (Exception e) {
            throw new DefaultGboatNestedException(e);
        }
    }
    
    /**
     * 将 Java 对象转换成 JSON 字符串,并保存到指定的文件中
     * 
     * @param file 目标输出流
     * @param obj 要转换成 JSON 字符串的 Java 对象。该 Java 对象的类定义必须使用 &#64;
     *            {@link JsonFilter} 注解进行标注
     * @param properties 要序列化成 JSON 字符串的属性
     * @throws IllegalArgumentException - 传入的 obj 对象的类定义没有使用 &#64;
     *             {@link JsonFilter} 注解进行标注时,抛出此异常
     */
    public static void object2Json(File file, Object obj, String[] properties) {
        try {
            getIncludeObjectWriter(obj.getClass(), properties).writeValue(file, obj);
        } catch (Exception e) {
            throw new DefaultGboatNestedException(e);
        }
    }
    
    /**
     * 将 Java 对象转换成 JSON 字符串,并保存到指定的文件中
     * 
     * @param file 目标输出流
     * @param obj 要转换成 JSON 字符串的 Java 对象。如果 excludeProperties 不为 null 且长度大于 0, 该
     *            Java 对象的类定义必须使用 &#64;{@link JsonFilter} 注解进行标注
     * @param excludeProperties 将 Java 对象序列化成 JSON 字符串时需要排除的属性
     * @throws IllegalArgumentException - excludeProperties 不为空,且传入的 obj
     *             对象的类定义没有使用 &#64;{@link JsonFilter} 注解进行标注时,抛出此异常
     */
    public static void object2JsonWithExcludeProps(File file, Object obj, String[] excludeProperties) {
        try {
            getExcludeObjectWriter(obj.getClass(), excludeProperties).writeValue(file, obj);
        } catch (Exception e) {
            throw new DefaultGboatNestedException(e);
        }
    }
    
    /**
     * 将字符串转成 JsonNode 对象
     * @param json JSON 字符串
     * @return JsonNode 对象
     */
    public static JsonNode fromString(String json) {
        return fromString(json, null);
    }
    
    /**
     * 将 JSON 字符串转换为 JsonNode 对象, 如果 JSON 字符串中没有某个属性,而默认值中存在,则从默认值中继承
     * @param json JSON 字符串
     * @param defaultValue 默认值的 JSON 字符串
     * @return 转换后的 JsonNode 对象
     */
    public static JsonNode fromString(String json, String defaultValue) {
        try {
            JsonNode result = MAPPER.readTree(json);
            if (StringUtils.isNotBlank(defaultValue) && (result instanceof ObjectNode)) {
                extend((ObjectNode) result, MAPPER.readTree(defaultValue));
            }
            return result;
        } catch (IOException e) {
            throw new DefaultGboatNestedException("解析 JSON 字符串失败", e);
        }
    }

    /**
     * 将资源文件中的 JSON 字符串读取为 JsonNode
     * @param resource 资源文件的路径
     * @return JsonNode 实例
     */
    public static JsonNode fromResource(String resource) {
        return fromResource(resource, null);
    }

    /**
     * Read a {@link JsonNode} from a resource path.
     * 
     * <p>
     * This method explicitly throws an {@link IOException} if the resource does
     * not exist, instead of letting a {@link NullPointerException} slip
     * through.
     * </p>
     * 
     * @param resource The path to the resource
     * @param classLoader 资源文件对应的类加载器
     * @return the JSON document at the resource
     */
    public static JsonNode fromResource(String resource, ClassLoader classLoader) {
        if (classLoader == null) {
            classLoader = Thread.currentThread().getContextClassLoader();
        }
        
        final URL url = classLoader.getResource(resource);
        if (url == null) {
            throw new DefaultGboatNestedException("resource " + resource + " not found");
        }

        return fromURL(url);
    }

    /**
     * Read a {@link JsonNode} from an URL.
     * 
     * @param url The URL to fetch the JSON document from
     * @return The document at that URL
     */
    public static JsonNode fromURL(URL url) {
        try {
            return MAPPER.readTree(url);
        } catch (IOException e) {
            throw new DefaultGboatNestedException("将 URL [" + url + "] 的内容转换为 JsonNode 失败。", e);
        }
    }

    /**
     * Read a {@link JsonNode} from a file on the local filesystem.
     * 
     * @param path the path (relative or absolute) to the file
     * @return the document in the file
     */
    public static JsonNode fromPath(String path) {
        return fromFile(new File(path));
    }

    /**
     * Same as {@link #fromPath(String)}, but this time the user supplies the
     * {@link File} object instead
     * 
     * @param file the File object
     * @return The document
     */
    public static JsonNode fromFile(File file) {
        try {
            return MAPPER.readTree(file);
        } catch (IOException e) {
            throw new DefaultGboatNestedException("将文件 [" + file + "] 的内容转换为 JsonNode 失败。", e);
        }
    }

    /**
     * Read a {@link JsonNode} from a user supplied {@link Reader}
     * 
     * @param reader The reader
     * @return the document
     */
    public static JsonNode fromReader(final Reader reader) {
        try {
            return MAPPER.readTree(reader);
        } catch (Exception e) {
            throw new DefaultGboatNestedException("从 Reader 加载 JSON 数据发生错误。", e);
        }
    }

    /**
     * 将输入流中的 JSON 字符串读取为 JsonNode 对象
     * @param in 包含 JSON 字符串的输入流
     * @return 转换后的 JsonNode 对象
     */
    public static JsonNode fromInputStream(InputStream in) {
        try {
            return MAPPER.readTree(in);
        } catch (Exception e) {
            throw new DefaultGboatNestedException("从输入流加载 JSON 数据发生错误。", e);
        }
    }

    private static ObjectWriter getIncludeObjectWriter(Class<?> clazz, String[] properties) {
        return getObjectWriter(clazz, properties, true);
    }

    private static ObjectWriter getExcludeObjectWriter(Class<?> clazz, String[] excludeProperties) {
        return getObjectWriter(clazz, excludeProperties, true);
    }
    
    private static ObjectWriter getObjectWriter(Class<?> clazz, String[] properties, boolean exclude) {
        // 如果是需要排除指定属性,且需要排除的属性列表为空,则直接返回默认的 ObjectWriter
        if (exclude && ArrayUtils.isEmpty(properties)) {
            return MAPPER.writer();
        }

        JsonFilter jsonFilter = clazz.getAnnotation(JsonFilter.class);
        if (jsonFilter == null) {
            throw new IllegalArgumentException(clazz + " 的类定义没有使用 @JsonFilter 注解进行标注时");
        }
        String filterId = jsonFilter.value();
        SimpleBeanPropertyFilter filter = exclude ? SimpleBeanPropertyFilter.serializeAllExcept(properties)
                : SimpleBeanPropertyFilter.filterOutAllExcept(properties);
        return MAPPER.writer(new SimpleFilterProvider().addFilter(filterId, filter));
    }
    
    /**
     * 实现 JsonNode 的深层继承,类似于 jQuery 的 extend 方法
     * @param obj 源对象
     * @param def 需要继承的 JsonNode 对象
     */
    public static void extend(ObjectNode obj, JsonNode def){
        Iterator<Entry<String, JsonNode>> defaultFields = def.fields();
        Entry<String, JsonNode> entry = null;
        while (defaultFields.hasNext()) {
            entry = defaultFields.next();
            String key = entry.getKey();
            JsonNode defVal = entry.getValue();
            if (defVal == null || defVal.getNodeType() == JsonNodeType.NULL) {
                continue;
            }

            JsonNode value = obj.get(key);
            if (value == null || value.getNodeType() == JsonNodeType.NULL) {
                obj.put(key, defVal);
            } else if (value instanceof ObjectNode) {
                extend((ObjectNode)value, defVal);
            }
        }
    }
}

JAXBUtil工具类代码

/**
 * Copyright By Grandsoft Company Limited.  
 * 2014年3月20日 下午4:31:41
 */
package gboat2.base.bridge.util.xml;

import gboat2.base.bridge.GboatAppConstants;
import gboat2.base.bridge.exception.DefaultGboatNestedException;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;
import java.util.Arrays;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.JAXBIntrospector;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.UnmarshallerHandler;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.util.JAXBSource;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLFilter;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLFilterImpl;

import com.thoughtworks.xstream.XStream;

/**
 * 使用 JAXB(Java Architecture for XML Binding) 实现 XML 与 JavaBean 之间相互转换的工具类。
 * <p>
 * 利用JAXB实现XML与Javabean之间的相互转换的大致思想:利用XML的生成相应的 XSD(XML Schema Definition,XSD) 或者 DTD(Documnet Type Definition),然后利用该 XSD 或 DTD 生成对应的 javabean 和 ObjectFactory。<br>
 * </p>
 * <pre>
 * 1. 利用XML生成相应XSD或者DTD
 *    1.1 生成 XSD:可以通过一个在线工具完成,网址: <a href="http://www.freeformatter.com/xsd-generator.html#ad-output" target="_blank">http://www.freeformatter.com/xsd-generator.html#ad-output</a>
 *    1.2 生成 DTD:可以使用一个java小工具,下载地址:<a href="http://sourceforge.net/projects/xml2dtd/" target="_blank">http://sourceforge.net/projects/xml2dtd/</a>
 *     
 * 2. 利用生成的 XSD 或者 DTD 生成 java 类
 *    2.1 命令行的方式(JDK6的支持):
 *        (a) XSD生成java: cmd --&gt; cd XSD所在文件夹--> xjc -d . –p 包名  ./xxx.xsd
 *       (b) DTD生成java: cmd --&gt; cd DTD所在文件夹 --> xjc -dtd  –d . –p 包名  ./xxx.dtd
 *    2.2 利用 Eclipse 生成 java 类(只适用于 XSD):
 *        选中 XSD 文件,右键 --&gt; Generate --&gt; JAXB Classes... 然后根据向导一步一步操作即可。
 * <b>注意:</b>XML 里面最好不要包含 value 等敏感关键字,如果包含,可先用别的名词替换,生成完 javabean 后再修改 javabean。
 * </pre>
 * <p>
 * <b>创建日期</b> 2014年3月20日
 * </p>
 * 
 * @author <a href="mailto:hemw@grandsoft.com.cn">何明旺</a>
 * @since 3.0
 */
public class JAXBUtil {
    
    /**
     * 创建 JAXB 编组实例
     * @param classesToBeBound 要通过 {@link JAXBContext} 进行编组的类
     * @return 编组对象
     */
    public static Marshaller createMarshaller(Class<?>... classesToBeBound){
        if(ArrayUtils.isEmpty(classesToBeBound)) {
            throw new IllegalArgumentException("传入的 class 为空");
        }
        
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(classesToBeBound);
            Marshaller marshaller = jaxbContext.createMarshaller();
            setMarshallerProperty(marshaller);
            return marshaller;
        } catch (JAXBException e) {
            throw new DefaultGboatNestedException("创建 JAXB 编组对象失败。classesToBeBound=" + Arrays.toString(classesToBeBound), e);
        }
    }
    
    /**
     * 创建 JAXB 编组实例
     * @param contextPath 上下文路径,由“:”连接的多个包名
     * @return 编组对象
     */
    public static Marshaller createMarshaller(String contextPath){
        return createMarshaller(contextPath, Thread.currentThread().getContextClassLoader());
    }
    
    /**
     * 创建 JAXB 编组实例
     * @param contextPath 上下文路径,由“:”连接的多个包名
     * @param classLoader 类加载器
     * @return 编组对象
     */
    public static Marshaller createMarshaller(String contextPath , ClassLoader classLoader){
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(contextPath, classLoader);
            Marshaller marshaller = jaxbContext.createMarshaller();
            setMarshallerProperty(marshaller);
            return marshaller;
        } catch (JAXBException e) {
            throw new DefaultGboatNestedException("创建 JAXB 编组对象失败。contextPath=" + contextPath, e);
        }
    }
    
    /**
     * 为 Marshaller 设置属性
     * @param marshaller 编组对象
     * @throws PropertyException
     */
    private static void setMarshallerProperty(Marshaller marshaller) throws PropertyException{
        marshaller.setProperty("com.sun.xml.bind.xmlDeclaration", Boolean.TRUE);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.setProperty("com.sun.xml.bind.characterEscapeHandler", new XmlCharacterEscapeHandler() );
        marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapperImpl());
    }
    
    /**
     * 创建 JAXB 解组实例
     * @param classesToBeBound 要通过 {@link JAXBContext} 进行解组的类
     * @return 解组对象
     */
    public static Unmarshaller createUnmarshaller(Class<?>... classesToBeBound){
        if(ArrayUtils.isEmpty(classesToBeBound)) {
            throw new IllegalArgumentException("传入的 class 为空");
        }
        
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(classesToBeBound);
            return jaxbContext.createUnmarshaller();
        } catch (JAXBException e) {
            throw new DefaultGboatNestedException("创建 JAXB 解组对象失败。classesToBeBound=" + Arrays.toString(classesToBeBound), e);
        }
    }
    
    /**
     * 创建 JAXB 解组实例
     * @param contextPath 上下文路径,由“:”连接的多个包名
     * @return 解组对象
     */
    public static Unmarshaller createUnmarshaller(String contextPath){
        return createUnmarshaller(contextPath, Thread.currentThread().getContextClassLoader());
    }
    
    /**
     * 创建 JAXB 解组实例
     * @param contextPath 上下文路径,由“:”连接的多个包名
     * @param classLoader 类加载器
     * @return 解组对象
     */
    public static Unmarshaller createUnmarshaller(String contextPath , ClassLoader classLoader){
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(contextPath, classLoader);
            return jaxbContext.createUnmarshaller();
        } catch (JAXBException e) {
            throw new DefaultGboatNestedException("创建 JAXB 解组对象失败。contextPath=" + contextPath, e);
        }
    }
    
    /**
     * 将 Java 对象转换为 XML 字符串
     * @param obj 要转换的 Java 对象
     * @return 转换后的 XML 字符串
     */
    public static String object2xml(Object obj) {
        StringWriter writer = new StringWriter();
        try {
            object2xml(obj, writer);
            return writer.toString();
        } finally {
            IOUtils.closeQuietly(writer);
        }
    }

    /**
     * 将 Java 对象转换为 XML
     * @param obj 要转换的 Java 对象
     * @param writer 输出对象
     */
    public static void object2xml(Object obj, Writer writer) {
        try {
            createMarshaller(obj.getClass()).marshal(wrapWithJAXB(obj), writer);
        } catch (JAXBException e) {
            throw new DefaultGboatNestedException("将 JAXB Element 转为 XML 失败。", e);
        }
    }
    
    public static String object2xmlByXStream(Object source, Class<?> dest) {
    	XStream xstream = new XStream();
    	xstream.alias(dest.getName(), source.getClass());
    	return xstream.toXML(source);
    }
    
    /**
     * 将 Java 对象转换为 XML,并保存到文件中
     * @param obj 要转换的 Java 对象
     * @param xmlFilePath XML 文件保存路径
     */
    public static void object2xml(Object obj, String xmlFilePath){
        object2xml(obj, new File(xmlFilePath));
    }
    
    /**
     * 将 Java 对象转换为 XML,并保存到文件中
     * @param obj 要转换的 Java 对象
     * @param xmlFile XML 文件
     */
    public static void object2xml(Object obj, File xmlFile){
        try {
            createMarshaller(obj.getClass()).marshal(wrapWithJAXB(obj), xmlFile);
        } catch (JAXBException e) {
            throw new DefaultGboatNestedException("将 JAXB Element 转为 XML 失败。", e);
        }
    }
    
    /**
     * 将 Java 对象转换为 XML,并保存到文件中
     * @param obj 要转换的 Java 对象
     * @param out 输出对象
     */
    public static void object2xml(Object obj, OutputStream out){
        try {
            createMarshaller(obj.getClass()).marshal(wrapWithJAXB(obj), out);
        } catch (JAXBException e) {
            throw new DefaultGboatNestedException("将 JAXB Element 转为 XML 失败。", e);
        }
    }
    
    /**
     * 将 XML 字符串转换成 Java 对象
     * @param xml XML 字符串
     * @param beanClass 目标 Java 类型
     * @param <T> XML 需要转换的目标对象的类型
     * @return 转换后的 JavaBean 实例
     */
    public static <T> T xml2object(String xml, Class<T> beanClass) {
        if (StringUtils.isBlank(xml)) {
            return null;
        }
        Reader reader = new StringReader(xml);
        try {
            return (T) xml2object(reader, beanClass);
        } finally {
            IOUtils.closeQuietly(reader);
        }
    }
    
    /**
     * 将 XML Reader 转换成 Java 对象
     *  (某些xml有类似ns2的namespace会导致转换失败,使用此方法可以转换)
     * @param reader 字符输入流
     * @param beanClass 目标 Java 类型
     * @param <T> XML 需要转换的目标对象的类型
     * @return 转换后的 JavaBean 实例
     * @throws SAXException 
     * @throws ParserConfigurationException 
     * @throws IOException 
     * @throws JAXBException 
     * @throws IllegalStateException 
     */
    @SuppressWarnings("unchecked")
    public static <T> T xml2objectIgnoreNameSpace(Unmarshaller unmarshaller,Reader reader, Class<T> beanClass) throws ParserConfigurationException, SAXException, IOException, IllegalStateException, JAXBException{
        Object result = null;
    	SAXParserFactory saxFactory = SAXParserFactory.newInstance();
        SAXParser saxParser = saxFactory.newSAXParser();
        XMLReader xmlReader = saxParser.getXMLReader();
        XMLFilter xmlFilter = new IgnoreNameSpaceXMLFilter(xmlReader);
        UnmarshallerHandler unmarshallerHandler = unmarshaller.getUnmarshallerHandler();
        xmlFilter.setContentHandler(unmarshallerHandler);
        xmlFilter.parse(new InputSource(reader));
        result = unmarshallerHandler.getResult();
        if (JAXBElement.class.equals(result.getClass())
                && !JAXBElement.class.equals(beanClass)
                && beanClass.isAssignableFrom(((JAXBElement<?>) result).getDeclaredType())) {
            return (T) JAXBIntrospector.getValue(result);
        }
        return (T) result;
    }

    /**
     * 将 XML Reader 转换成 Java 对象
     * @param reader 字符输入流
     * @param beanClass 目标 Java 类型
     * @param <T> XML 需要转换的目标对象的类型
     * @return 转换后的 JavaBean 实例
     */
    @SuppressWarnings("unchecked")
    public static <T> T xml2object(Reader reader, Class<T> beanClass){
        Object result = null;
        try {
        	Unmarshaller unmarshaller = createUnmarshaller(beanClass);
			result = unmarshaller.unmarshal(reader);
        } catch (JAXBException e) {
            throw new DefaultGboatNestedException("将 XML 转为 Java 对象失败,beanClass=" + beanClass, e);
        } 
        
        if (JAXBElement.class.equals(result.getClass())
                && !JAXBElement.class.equals(beanClass)
                && beanClass.isAssignableFrom(((JAXBElement<?>) result).getDeclaredType())) {
            return (T) JAXBIntrospector.getValue(result);
        }
        return (T) result;
    }

    /**
     * 将 XML 文件转换成 Java 对象
     * @param xmlFile XML 文件
     * @param beanClass 目标 Java 类型
     * @param <T> XML 需要转换的目标对象的类型
     * @return 转换后的 JavaBean 实例
     */
    @SuppressWarnings("unchecked")
    public static  <T> T xml2object(File xmlFile, Class<T> beanClass){
        Object result = null;
        try {
            result = createUnmarshaller(beanClass).unmarshal(xmlFile);
        } catch (JAXBException e) {
            throw new DefaultGboatNestedException("将 XML 转为 Java 对象失败,beanClass=" + beanClass + ", xmlFile=" + xmlFile, e);
        }
        
        if (JAXBElement.class.equals(result.getClass())
                && !JAXBElement.class.equals(beanClass)
                && beanClass.isAssignableFrom(((JAXBElement<?>) result).getDeclaredType())) {
            return (T) JAXBIntrospector.getValue(result);
        }
        return (T) result;
    }

    /**
     * 将 URL 中的 XML 数据换成 Java 对象
     * @param url XML 文件对应的 URL
     * @param beanClass 目标 Java 类型
     * @param <T> XML 需要转换的目标对象的类型
     * @return 转换后的 JavaBean 实例
     */
    @SuppressWarnings("unchecked")
    public static <T> T xml2object(URL url, Class<T> beanClass){
        Object result = null;
        try {
            result = createUnmarshaller(beanClass).unmarshal(url);
        } catch (JAXBException e) {
            throw new DefaultGboatNestedException("将 XML 转为 Java 对象失败,beanClass=" + beanClass + ", url=" + url, e);
        }
        
        if (JAXBElement.class.equals(result.getClass())
                && !JAXBElement.class.equals(beanClass)
                && beanClass.isAssignableFrom(((JAXBElement<?>) result).getDeclaredType())) {
            return (T) JAXBIntrospector.getValue(result);
        }
        return (T) result;
    }

    /**
     * 将输入流中的 XML 数据换成 Java 对象
     * @param input XML 数据输入流
     * @param beanClass 目标 Java 类型
     * @param <T> XML 需要转换的目标对象的类型
     * @return 转换后的 JavaBean 实例
     */
    @SuppressWarnings("unchecked")
    public static <T> T xml2object(InputStream input, Class<T> beanClass){
        Object result = null;
        try {
            result = createUnmarshaller(beanClass).unmarshal(input);
        } catch (JAXBException e) {
            throw new DefaultGboatNestedException("将 XML 转为 Java 对象失败,beanClass=" + beanClass, e);
        }
        
        if (JAXBElement.class.equals(result.getClass())
                && !JAXBElement.class.equals(beanClass)
                && beanClass.isAssignableFrom(((JAXBElement<?>) result).getDeclaredType())) {
            return (T) JAXBIntrospector.getValue(result);
        }
        return (T) result;
    }

    /**
     * 深度复制 JAXB 对象
     * @param jaxbObj 源对象
     * @param <T> 需要复制的目标对象的类型
     * @return 克隆出来的一个新对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T deepCopyJAXB(T jaxbObj) {
        if (jaxbObj == null) {
            return null;
        }

        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(jaxbObj.getClass());
            Object wrappedObj = wrapWithJAXB(jaxbObj);
            JAXBSource source = new JAXBSource(jaxbContext, wrappedObj);
            Object result = jaxbContext.createUnmarshaller().unmarshal(source);
            return wrappedObj.equals(jaxbObj) ? (T) result : (T) JAXBIntrospector.getValue(result);
        } catch (JAXBException e) {
            throw new DefaultGboatNestedException("深度复制 JAXB Element 失败。", e);
        }
    }

    /**
     * 将不能直接用于 JAXB 编组的 Java 对象包装成 JAXBElement 对象
     * <p>
     * 如果被编组的POJO没有使用@XmlRootElement注解,则编排会失败,可以使用以下的方式,通过增加QName的方式来解决这个问题
     * @param obj
     * @return
     */
    @SuppressWarnings("unchecked")
    private static <T> Object wrapWithJAXB(T obj){
        if(obj == null) {
            return null;
        }
        
        if(obj instanceof JAXBElement) {
            return obj;
        }
        
        Class<T> clazz = (Class<T>) obj.getClass();
        if(clazz.getAnnotation(XmlRootElement.class) != null) {
            return obj;
        }

        String namespaceURI = getNameSpace(clazz);
        String localPart = getLocalPart(clazz);
        
        return new JAXBElement<T>(new QName(namespaceURI, localPart), clazz, obj);
    }
    
    /**
     * 获取构成QName的LocalPart
     * @param clazz
     * @return
     */
    private static String getLocalPart(Class<?> clazz) {
    	String localPart = null;
        // 源对象的 @XmlType 注解
        XmlType sourceXmlType = clazz.getAnnotation(XmlType.class);
        if (sourceXmlType != null) {
            localPart = sourceXmlType.name();
        }

        if (localPart == null || "##default".equals(localPart)) {
            localPart = WordUtils.uncapitalize(clazz.getSimpleName());
        }
        
        return localPart;
    }
    
    /**
     * 获取构成QName的Namespace
     * @param clazz
     * @return
     */
    private static String getNameSpace(Class<?> clazz) {
        String namespaceURI = null;
        // 源对象的 @XmlType 注解
        XmlType sourceXmlType = clazz.getAnnotation(XmlType.class);
        if (sourceXmlType != null) {
            namespaceURI = sourceXmlType.namespace();
        }

        // 如果源对象的 @XmlType 注解没有明确指定 namespace 或 name
        if (namespaceURI == null || "##default".equals(namespaceURI)) {
            namespaceURI = XMLConstants.NULL_NS_URI;
        }
        
        return namespaceURI;
    }
    
    /**
     * 跨类型复制 JAXBElement Bean(深度复制)
     * @param source 源对象
     * @param destType 目标类型
     * @param <T> 需要复制的目标对象的类型
     * @return 与源对象有相同属性值的目标对象
     */
    public static <T> T castClone(Object source, Class<T> destType) {
        return (T)castClone(source, destType, null);
    }
    
    /**
     * 跨类型复制 JAXBElement Bean(深度复制)
     * @param source 源对象
     * @param destType 目标类型
     * @param <T> 需要复制的目标对象的类型
     * @return 与源对象有相同属性值的目标对象
     */
    public static <T> T castClone(Object source, Class<T> destType,ClassLoader destClassLoader) {
        return (T)castClone(source, destType, false,destClassLoader);
    }
    
    /**
     * 跨类型复制 JAXBElement Bean(深度复制)
     * @param source 源对象
     * @param destType 目标类型
     * @param <T> 需要复制的目标对象的类型
     * @return 与源对象有相同属性值的目标对象
     */ 
    public static <T> T castClone(Object source, Class<T> destType,boolean ignoreNameSpace) {
        return (T)castClone(source, destType, ignoreNameSpace,null);
    }
    
    /**
     * 跨类型复制 JAXBElement Bean(深度复制)
     * @param source 源对象
     * @param destType 目标类型
     * @param destClassLoader 加载目标类的 ClassLoader
     * @param <T> 需要复制的目标对象的类型
     * @return 与源对象有相同属性值的目标对象
     */
    @SuppressWarnings({ "unchecked"})
    public static <T> T castClone(Object source, Class<T> destType,boolean ignoreNameSpace, ClassLoader destClassLoader) {
        if(source == null) {
            return null;
        } 
        Object result = null;
        String xml = object2xml(source);
        xml = xml.replaceAll("[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]", ""); 
        Unmarshaller unmarshaller = (destClassLoader == null) ? createUnmarshaller(destType) : createUnmarshaller(destType.getPackage().getName(), destClassLoader);
        StringReader reader = null;
        reader = new StringReader(xml);
        if(ignoreNameSpace){
         try {
			result = xml2objectIgnoreNameSpace(unmarshaller,reader,destType);
		} catch (IllegalStateException e) {
            throw new DefaultGboatNestedException("通过 JAXB 将 XML 转换成 JavaBean 失败,destType=" + destType + ", xml=\n" + xml, e);
		} catch (ParserConfigurationException e) {
            throw new DefaultGboatNestedException("通过 JAXB 将 XML 转换成 JavaBean 失败,destType=" + destType + ", xml=\n" + xml, e);
		} catch (SAXException e) {
            throw new DefaultGboatNestedException("通过 JAXB 将 XML 转换成 JavaBean 失败,destType=" + destType + ", xml=\n" + xml, e);
		} catch (IOException e) {
            throw new DefaultGboatNestedException("通过 JAXB 将 XML 转换成 JavaBean 失败,destType=" + destType + ", xml=\n" + xml, e);
		} catch (JAXBException e) {
            throw new DefaultGboatNestedException("通过 JAXB 将 XML 转换成 JavaBean 失败,destType=" + destType + ", xml=\n" + xml, e);
		} 
        }else{
             try {
                 XMLInputFactory xmlFactory  = XMLInputFactory.newInstance();  
                 XMLStreamReader reader2 = xmlFactory.createXMLStreamReader(reader); 
                 // 采用这种方式则不需要java对象类必须使用@XmlRootElement注解的问题
                 JAXBElement userElement = unmarshaller.unmarshal(reader2, destType);
                 result = userElement.getValue();
             } catch (Exception e) {
                 throw new DefaultGboatNestedException("通过 JAXB 将 XML 转换成 JavaBean 失败,destType=" + destType + ", xml=\n" + xml, e);
             } finally {
                 IOUtils.closeQuietly(reader);
             }
        }
       

        return (T)result;
    }
    
    
    private static class IgnoreNameSpaceXMLFilter extends XMLFilterImpl {

        public IgnoreNameSpaceXMLFilter(XMLReader xmlReader) {
            super(xmlReader);
        }

        @Override
        public void startElement(String uri, String localName, String qName,
                Attributes attributes) throws SAXException {
            int colonIndex = qName.indexOf(':');
            if(colonIndex >= 0) {
                qName = qName.substring(colonIndex + 1);
            }
            super.startElement(uri, localName, qName, attributes);
        }

        @Override
        public void endElement(String uri, String localName, String qName)
                throws SAXException {
            int colonIndex = qName.indexOf(':');
            if(colonIndex >= 0) {
                qName = qName.substring(colonIndex + 1);
            }
            super.endElement(uri, localName, qName);
        }

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值