如何改造Java方法使其支持传入任意类型对象用于JAXB序列化
问题根因
Java泛型采用类型擦除机制,编译后泛型类型T的信息会被擦除为Object,无法在运行时获取到T对应的Class对象,因此T.class属于不合法的写法。
解决方案
方案1:显式传入Class类型参数(兼容性最强)
调用方法时额外传入对应模型类的Class对象即可,代码实现如下:
import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.namespace.QName; import java.io.StringWriter; public <T> String getXmlBodyForRequest(T model, Class<T> clazz) throws JAXBException { StringWriter sw = new StringWriter(); JAXBContext jaxbContext = JAXBContext.newInstance(clazz); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); QName qName = new QName("com.companyname.testdata", "demo"); JAXBElement<T> root = new JAXBElement<>(qName, clazz, model); jaxbMarshaller.marshal(root, sw); return sw.toString(); }
调用示例:
String xml = getXmlBodyForRequest(patchRequest, PatchRequest.class);
方案2:直接从入参实例获取Class(调用更简便)
如果你的模型类都是普通POJO、不存在动态代理的场景,可以直接通过入参对象的getClass()方法获取类型,不需要额外传参:
import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.namespace.QName; import java.io.StringWriter; public <T> String getXmlBodyForRequest(T model) throws JAXBException { StringWriter sw = new StringWriter(); Class<?> clazz = model.getClass(); JAXBContext jaxbContext = JAXBContext.newInstance(clazz); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); QName qName = new QName("com.companyname.testdata", "demo"); JAXBElement<T> root = new JAXBElement(qName, clazz, model); jaxbMarshaller.marshal(root, sw); return sw.toString(); }
注意:如果模型类可能被CGLIB等动态代理框架生成代理子类(比如Spring管理的Bean、ORM框架返回的代理对象等),model.getClass()会返回代理子类的Class,无法正确序列化,这种场景建议使用方案1。
优化建议
如果不同模型类需要不同的XML根节点命名空间和节点名,可以把QName的两个参数(命名空间、根节点名)也改成方法入参,进一步提升方法的通用性。
内容的提问来源于stack exchange,提问作者amadain
相关产品推荐
相关产品推荐

