使用命名空间和前缀的 JAXB 解组
Posted
技术标签:
【中文标题】使用命名空间和前缀的 JAXB 解组【英文标题】:JAXB unmarshall with namespaces and prefix 【发布时间】:2013-10-24 02:15:48 【问题描述】:我正在使用 JAXB 从 SOAP 响应中解析 xml 元素。我已经为 xml 元素定义了 POJO 类。我已经测试了没有命名空间的 pojo 类,并且前缀它的工作正常。尽管当我尝试使用命名空间和前缀解析时遇到以下异常。要求是解析来自 SOAPMessage 对象的输入
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Envelope"). Expected elements are <Envelope>
尝试通过为 package-info.java 中的包创建 @XMLSchema 并将此文件定位在包文件夹中来修复。谁能指导我继续前进?
Referred这个帖子但对我没有帮助。
已编辑:XMLSchema
@javax.xml.bind.annotation.XmlSchema (
xmlns = @javax.xml.bind.annotation.XmlNs(prefix = "env",
namespaceURI="http://schemas.xmlsoap.org/soap/envelope/"),
@javax.xml.bind.annotation.XmlNs(prefix="ns3", namespaceURI="http://www.xxxx.com/ncp/oomr/dto/")
)
package com.one.two;
提前致谢
【问题讨论】:
您能说明一下您是如何在@XMLSchema 中配置命名空间的吗? 信封元素呢?命名空间是否正确? 您最终是将信封映射到对象还是仅映射到身体?以下是映射到 SOAP 文档中间的方法:blog.bdoughan.com/2012/08/… @BlaiseDoughan 我已经参考了您的许多关于 xml 的博客。我是否应该从 SOAPMessage 对象创建文件?是否可以从 Object 解析中间元素?是的,我想从正文中读取元素,, 【参考方案1】:这可以在不使用标准 SOAPMessage 类修改生成的 JAXB 代码的情况下完成。我写过这个here和here
这有点繁琐,但可以正常工作。
编组
Farm farm = new Farm();
farm.getHorse().add(new Horse());
farm.getHorse().get(0).setName("glue factory");
farm.getHorse().get(0).setHeight(BigInteger.valueOf(123));
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Marshaller marshaller = JAXBContext.newInstance(Farm.class).createMarshaller();
marshaller.marshal(farm, document);
SOAPMessage soapMessage = MessageFactory.newInstance().createMessage();
soapMessage.getSOAPBody().addDocument(document);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
soapMessage.writeTo(outputStream);
String output = new String(outputStream.toByteArray());
解组
String example =
"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Header /><soapenv:Body><ns2:farm xmlns:ns2=\"http://adamish.com/example/farm\"><horse height=\"123\" name=\"glue factory\"/></ns2:farm></soapenv:Body></soapenv:Envelope>";
SOAPMessage message = MessageFactory.newInstance().createMessage(null,
new ByteArrayInputStream(example.getBytes()));
Unmarshaller unmarshaller = JAXBContext.newInstance(Farm.class).createUnmarshaller();
Farm farm = (Farm)unmarshaller.unmarshal(message.getSOAPBody().extractContentAsDocument());
【讨论】:
很好的答案,对于任何可能感兴趣的人来说,一个小小的补充:如果你不知道确切的类将是 SOAP 信封中的那个,但你知道信封,像这样初始化解组器:JAXBContext.newInstance("com.my.package").createUnmarshaller()
如果这对任何人都有帮助,我会不断出错,直到我明确告诉解组器(再次)解组到哪个类。 Farm farm = unmarshaller.unmarshal(message.getSOAPBody().extractContentAsDocument(), Farm.class).getValue();
【参考方案2】:
以下是您可以如何处理您的使用 cae:
如果您需要映射Envelope
元素
包裹信息
通常您会使用@XmlSchema
,如下所示。像我所做的那样使用namespace
和elementFormDefault
属性意味着映射到XML 元素的所有数据,除非另有映射,否则将属于http://www.xxxx.com/ncp/oomr/dto/
命名空间。 xmlns
中指定的信息用于 XML 模式生成,尽管一些 JAXB 实现使用它来确定编组时命名空间的首选前缀(请参阅:http://blog.bdoughan.com/2011/11/jaxb-and-namespace-prefixes.html)。
@XmlSchema (
namespace="http://www.xxxx.com/ncp/oomr/dto/",
elementFormDefault=XmlNsForm.QUALIFIED,
xmlns =
@XmlNs(prefix = "env", namespaceURI="http://schemas.xmlsoap.org/soap/envelope/"),
@XmlNs(prefix="whatever", namespaceURI="http://www.xxxx.com/ncp/oomr/dto/")
)
package com.one.two;
import javax.xml.bind.annotation.*;
信封
如果在com.one.two
中您需要映射到来自http://www.xxxx.com/ncp/oomr/dto/
以外的命名空间中的元素,那么您需要在@XmlRootElement
和@XmlElement
注释中指定它。
package com.one.two;
import javax.xml.bind.annotation.*;
@XmlRootElement(name="Envelope", namespace="http://schemas.xmlsoap.org/soap/envelope/")
@XmlAccessorType(XmlAccessType.FIELD)
public class Envelope
@XmlElement(name="Body", namespace="http://schemas.xmlsoap.org/soap/envelope/")
private Body body;
更多信息
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html如果您只想映射身体
您可以使用 StAX 解析器来解析消息并前进到有效负载部分并从那里解组:
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class UnmarshalDemo
public static void main(String[] args) throws Exception
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource xml = new StreamSource("src/blog/stax/middle/input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
xsr.nextTag();
while(!xsr.getLocalName().equals("return"))
xsr.nextTag();
JAXBContext jc = JAXBContext.newInstance(Customer.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement<Customer> jb = unmarshaller.unmarshal(xsr, Customer.class);
xsr.close();
更多信息
http://blog.bdoughan.com/2012/08/handle-middle-of-xml-document-with-jaxb.html【讨论】:
@TheUnusual -package-info.java
与同一包的其他 Java 源位于同一目录中。
感谢您的回复@Blaise Doughan。
Blaise,非常感谢这篇文章,它对我帮助很大。在我的例子中,我的 SOAP 信封包含带有文本的元素,在这种情况下,对 getLocalName() 的调用会在无效内容上引发异常。我对您的 while 循环进行了小改动以解决此问题: while (xsr.hasNext()) xsr.next(); if (xsr.hasName() && xsr.getLocalName().equals("return")) 中断;
【参考方案3】:
只是想添加到现有的答案——如果 XML 文档不知道命名空间,则在解组时您可能会收到错误:javax.xml.bind.UnmarshalException: unexpected element (uri:"http://some.url";, local :"someOperation")
如果是这种情况,您可以简单地在解组器上使用不同的方法:
Unmarshaller unmarshaller = JAXBContext.newInstance(YourObject.class).createUnmarshaller();
JAXBElement<YourObject> element = unmarshaller.unmarshal(message.getSOAPBody().extractContentAsDocument(), YourObject.class);
YourObject yo = element.getValue();
【讨论】:
以上是关于使用命名空间和前缀的 JAXB 解组的主要内容,如果未能解决你的问题,请参考以下文章