使用JAXB反序列化BPMN XML为Java对象时遇命名空间错误
解决Camunda BPMN XML JAXB反序列化命名空间不匹配错误
问题场景
从Camunda引擎获取BPMN XML响应,使用JAXB Unmarshaller将XML字符串转换为Java对象时,触发命名空间不匹配错误。
错误日志
Exception in thread "main" jakarta.xml.bind.UnmarshalException: unexpected element (uri:"http://www.omg.org/spec/BPMN/20100524/MODEL", local:"definitions"). Expected elements are <{}bpmn:definitions> at org.glassfish.jaxb.runtime.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:701) at org.glassfish.jaxb.runtime.v2.runtime.unmarshaller.Loader.reportError(Loader.java:230) at org.glassfish.jaxb.runtime.v2.runtime.unmarshaller.Loader.reportError(Loader.java:225) at org.glassfish.jaxb.runtime.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:92) at org.glassfish.jaxb.runtime.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1106) at org.glassfish.jaxb.runtime.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:531) at org.glassfish.jaxb.runtime.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:513) at org.glassfish.jaxb.runtime.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:137) at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:518) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:374) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl$NSContentDriver.scanRootElementHook(XMLNSDocumentScannerImpl.java:613) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:3079)
待解析的XML示例
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_1xqdoqa" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.13.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.19.0"> <bpmn:process id="Process2" isExecutable="true" camunda:historyTimeToLive="1"> <bpmn:userTask id="Activity_0r63cqs"> <bpmn:incoming>Flow02</bpmn:incoming> <bpmn:outgoing>Flow_0xsmp8q</bpmn:outgoing> </bpmn:userTask> <bpmn:userTask id="Activity_0e7h96z"> <bpmn:extensionElements> <camunda:taskListener class="ir.edmp.camunda.listeners.EDMPTaskCreateListener" event="create"> <camunda:field name="assigneeType"> <camunda:string>Static</camunda:string> </camunda:field> <camunda:field name="assigneeApproach"> <camunda:string>admin_platform</camunda:string> </camunda:field> </camunda:taskListener> .... </bpmn:definitions>
原Java实体类(BPMNDefinitions)
@XmlRootElement(name = "bpmn:definitions") public class BPMNDefinitions { private String bPMN; private String xPMNDI; private String dC; private String camunda; private String xSI; private String dI; private String modeler; private String id; private String targetNameSpace; private String exporter; private String exporterVersion; private String executionPlatform; private String executionPlatformVersion; public String getbPMN() { return bPMN; } @XmlAttribute public void setbPMN(String bPMN) { this.bPMN = bPMN; } public String getxPMNDI() { return xPMNDI; } @XmlAttribute public void setxPMNDI(String xPMNDI) { this.xPMNDI = xPMNDI; } public String getdC() { return dC; } @XmlAttribute public void setdC(String dC) { this.dC = dC; } public String getCamunda() { return camunda; } @XmlAttribute public void setCamunda(String camunda) { this.camunda = camunda; } public String getxSI() { return xSI; } @XmlAttribute public void setxSI(String xSI) { this.xSI = xSI; } public String getdI() { return dI; } @XmlAttribute public void setdI(String dI) { this.dI = dI; } public String getModeler() { return modeler; } @XmlAttribute public void setModeler(String modeler) { this.modeler = modeler; } public String getId() { return id; } @XmlAttribute public void setId(String id) { this.id = id; } public String getTargetNameSpace() { return targetNameSpace; } @XmlAttribute public void setTargetNameSpace(String targetNameSpace) { this.targetNameSpace = targetNameSpace; } public String getExporter() { return exporter; } @XmlAttribute public void setExporter(String exporter) { this.exporter = exporter; } public String getExporterVersion() { return exporterVersion; } @XmlAttribute public void setExporterVersion(String exporterVersion) { this.exporterVersion = exporterVersion; } public String getExecutionPlatform() { return executionPlatform; } @XmlAttribute public void setExecutionPlatform(String executionPlatform) { this.executionPlatform = executionPlatform; } public String getExecutionPlatformVersion() { return executionPlatformVersion; } @XmlAttribute public void setExecutionPlatformVersion(String executionPlatformVersion) { this.executionPlatformVersion = executionPlatformVersion; } }
反序列化代码
public static void main(String[] args) throws IOException, ParseException, JAXBException { HttpGet getRequest = new HttpGet("http://localhost:8081/engine-rest/process-definition/key/Process2/xml"); CloseableHttpClient client = HttpClientBuilder.create().build(); CloseableHttpResponse response = client.execute(getRequest); JSONObject firstTaskFlows = new JSONObject(EntityUtils.toString(response.getEntity())); String xmlString = firstTaskFlows.get("bpmn20Xml").toString(); JAXBContext jaxbContext = JAXBContext.newInstance(BPMNDefinitions.class); StringReader reader = new StringReader(xmlString); Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); BPMNDefinitions customer = (BPMNDefinitions) jaxbUnmarshaller.unmarshal(reader); }
解决方案
错误原因
XML中的<bpmn:definitions>属于命名空间http://www.omg.org/spec/BPMN/20100524/MODEL,前缀bpmn只是该命名空间的别名。原代码用@XmlRootElement(name = "bpmn:definitions")绑定带前缀的标签名,而非实际命名空间,导致JAXB无法匹配XML元素。
修正步骤
修正根元素的命名空间绑定
将@XmlRootElement改为指定实际命名空间URI和本地名称,而非带前缀的名称:@XmlRootElement(name = "definitions", namespace = "http://www.omg.org/spec/BPMN/20100524/MODEL") public class BPMNDefinitions { // ... 其他代码 }添加包级命名空间映射
在实体类所在的包下创建package-info.java文件,声明命名空间前缀与URI的映射,确保JAXB能识别XML中的前缀:@XmlSchema( namespace = "http://www.omg.org/spec/BPMN/20100524/MODEL", elementFormDefault = XmlNsForm.QUALIFIED, xmlns = { @XmlNs(prefix = "bpmn", namespaceURI = "http://www.omg.org/spec/BPMN/20100524/MODEL"), @XmlNs(prefix = "modeler", namespaceURI = "http://camunda.org/schema/modeler/1.0"), @XmlNs(prefix = "camunda", namespaceURI = "http://camunda.org/schema/1.0/bpmn") } ) package com.your.package; // 替换为实体类实际所在包名 import jakarta.xml.bind.annotation.XmlNs; import jakarta.xml.bind.annotation.XmlNsForm; import jakarta.xml.bind.annotation.XmlSchema;修正带命名空间前缀的属性绑定
XML中modeler:executionPlatform这类带前缀的属性,需要通过@XmlAttribute的namespace指定对应的URI:public String getExecutionPlatform() { return executionPlatform; } @XmlAttribute(namespace = "http://camunda.org/schema/modeler/1.0") public void setExecutionPlatform(String executionPlatform) { this.executionPlatform = executionPlatform; } public String getExecutionPlatformVersion() { return executionPlatformVersion; } @XmlAttribute(namespace = "http://camunda.org/schema/modeler/1.0") public void setExecutionPlatformVersion(String executionPlatformVersion) { this.executionPlatformVersion = executionPlatformVersion; }删除无效的命名空间声明属性
XML中的xmlns:bpmn、xmlns:modeler等是命名空间声明,属于XML语法的一部分,不需要手动绑定到Java属性。删除原实体类中bPMN、xPMNDI、dC、camunda、xSI、dI、modeler这些对应命名空间声明的属性及getter/setter方法。
最终修正后的BPMNDefinitions类示例
@XmlRootElement(name = "definitions", namespace = "http://www.omg.org/spec/BPMN/20100524/MODEL") public class BPMNDefinitions { private String id; private String targetNamespace; private String exporter; private String exporterVersion; private String executionPlatform; private String executionPlatformVersion; public String getId() { return id; } @XmlAttribute public void setId(String id) { this.id = id; } public String getTargetNamespace() { return targetNamespace; } @XmlAttribute public void setTargetNamespace(String targetNamespace) { this.targetNamespace = targetNamespace; } public String getExporter() { return exporter; } @XmlAttribute public void setExporter(String exporter) { this.exporter = exporter; } public String getExporterVersion() { return exporterVersion; } @XmlAttribute public void setExporterVersion(String exporterVersion) { this.exporterVersion = exporterVersion; } public String getExecutionPlatform() { return executionPlatform; } @XmlAttribute(namespace = "http://camunda.org/schema/modeler/1.0") public void setExecutionPlatform(String executionPlatform) { this.executionPlatform = executionPlatform; } public String getExecutionPlatformVersion() { return executionPlatformVersion; } @XmlAttribute(namespace = "http://camunda.org/schema/modeler/1.0") public void setExecutionPlatformVersion(String executionPlatformVersion) { this.executionPlatformVersion = executionPlatformVersion; } }
内容的提问来源于stack exchange,提问作者Matin Parsapour
相关产品推荐
相关产品推荐

