【问题标题】:How make JAXB generate XML correctly?如何让 JAXB 正确生成 XML?
【发布时间】:2014-06-26 20:21:19
【问题描述】:

我正在尝试使用在soap 请求中使用两个命名空间的网络服务,但它不接受任何前缀。我正在使用 cxf 来生成客户端和数据绑定。当使用 JAXB 作为默认数据绑定时,这是发送到 Web 服务服务器的消息:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
    <soap:Header>
        <nfeCabecMsg xmlns:ns2="http://www.portalfiscal.inf.br/nfe"
                     xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/NfeStatusServico2">
            <cUF>31</cUF>
            <versaoDados>2.00</versaoDados>
        </nfeCabecMsg>
    </soap:Header>
    <soap:Body>
        <nfeDadosMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/NfeStatusServico2"
                     xmlns:ns2="http://www.portalfiscal.inf.br/nfe">
            <ns2:consStatServ versao="2.00">
                <ns2:tpAmb>2</ns2:tpAmb>
                <ns2:cUF>31</ns2:cUF>
                <ns2:xServ>STATUS</ns2:xServ>
            </ns2:consStatServ>
        </nfeDadosMsg>
    </soap:Body>
</soap:Envelope>

但这不是服务器期望的格式。使用 XmlBean 作为默认数据绑定,我可以正确生成消息(如服务器所期望的那样):

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
    <soap:Header>
        <nfeCabecMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/NfeStatusServico2">
            <cUF>31</cUF>
            <versaoDados>2.00</versaoDados>
        </nfeCabecMsg>
    </soap:Header>
    <soap:Body>
        <nfeDadosMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/NfeStatusServico2">
            <consStatServ xmlns="http://www.portalfiscal.inf.br/nfe" versao="2.00">
                <tpAmb>2</tpAmb>
                <cUF>31</cUF>
                <xServ>STATUS</xServ>
            </consStatServ>
        </nfeDadosMsg>
    </soap:Body>
</soap:Envelope>

但是,我真的很害怕使用 XmlBeans,因为我的软件的另一个重要部分是使用 JAXB 开发的,我可能需要将其更改为 XmlBeans。在我看到 XmlBeans 的最后一个版本是在 2012 年之后,我不相信 XmlBeans 将来会得到支持。 有没有办法可以使用 JAXB 正确生成消息??

更新

这是网络服务的 wsdl:

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
              xmlns:tns="http://www.portalfiscal.inf.br/nfe/wsdl/NfeStatusServico2"
              xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
              targetNamespace="http://www.portalfiscal.inf.br/nfe/wsdl/NfeStatusServico2"
              xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    <wsdl:types>
        <s:schema elementFormDefault="qualified"
                  targetNamespace="http://www.portalfiscal.inf.br/nfe/wsdl/NfeStatusServico2">
            <s:element name="nfeDadosMsg">
                <s:complexType mixed="true">
                    <s:sequence>
                        <s:any/>
                    </s:sequence>
                </s:complexType>
            </s:element>
            <s:element name="nfeStatusServicoNF2Result">
                <s:complexType mixed="true">
                    <s:sequence>
                        <s:any/>
                    </s:sequence>
                </s:complexType>
            </s:element>
            <s:element name="nfeCabecMsg" type="tns:nfeCabecMsg"/>
            <s:complexType name="nfeCabecMsg">
                <s:sequence>
                    <s:element minOccurs="0" maxOccurs="1" name="cUF" type="s:string"/>
                    <s:element minOccurs="0" maxOccurs="1" name="versaoDados" type="s:string"/>
                </s:sequence>
                <s:anyAttribute/>
            </s:complexType>
        </s:schema>
    </wsdl:types>
    <wsdl:message name="nfeStatusServicoNF2Soap12In">
        <wsdl:part name="nfeDadosMsg" element="tns:nfeDadosMsg"/>
    </wsdl:message>
    <wsdl:message name="nfeStatusServicoNF2Soap12Out">
        <wsdl:part name="nfeStatusServicoNF2Result" element="tns:nfeStatusServicoNF2Result"/>
    </wsdl:message>
    <wsdl:message name="nfeStatusServicoNF2nfeCabecMsg">
        <wsdl:part name="nfeCabecMsg" element="tns:nfeCabecMsg"/>
    </wsdl:message>
    <wsdl:portType name="NfeStatusServico2Soap12">
        <wsdl:operation name="nfeStatusServicoNF2">
            <wsdl:input message="tns:nfeStatusServicoNF2Soap12In"/>
            <wsdl:output message="tns:nfeStatusServicoNF2Soap12Out"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="NfeStatusServico2Soap12" type="tns:NfeStatusServico2Soap12">
        <soap12:binding transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="nfeStatusServicoNF2">
            <soap12:operation soapAction="http://www.portalfiscal.inf.br/nfe/wsdl/NfeStatusServico2/nfeStatusServicoNF2"
                              style="document"/>
            <wsdl:input>
                <soap12:body use="literal"/>
                <soap12:header message="tns:nfeStatusServicoNF2nfeCabecMsg" part="nfeCabecMsg" use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap12:body use="literal"/>
                <soap12:header message="tns:nfeStatusServicoNF2nfeCabecMsg" part="nfeCabecMsg" use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="NfeStatusServico2">
        <wsdl:port name="NfeStatusServico2Soap12" binding="tns:NfeStatusServico2Soap12">
            <soap12:address location="https://nfe.sefazvirtual.rs.gov.br/ws/NfeStatusServico/NfeStatusServico2.asmx"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

这些是用于创建 consStatServ 标记的最相关的模式:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://www.portalfiscal.inf.br/nfe" xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.portalfiscal.inf.br/nfe" elementFormDefault="qualified"
           attributeFormDefault="unqualified">
    <xs:include schemaLocation="leiauteConsStatServ_v2.00.xsd"/>
    <xs:element name="consStatServ" type="TConsStatServ">
        <xs:annotation>
            <xs:documentation>Schema XML de validação do Pedido de Consulta do Status do Serviço</xs:documentation>
        </xs:annotation>
    </xs:element>
</xs:schema>


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://www.portalfiscal.inf.br/nfe" xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.portalfiscal.inf.br/nfe" elementFormDefault="qualified"
           attributeFormDefault="unqualified">
    <xs:include schemaLocation="tiposBasico_v1.03.xsd"/>
    <xs:complexType name="TConsStatServ">
        <xs:annotation>
            <xs:documentation>Tipo Pedido de Consulta do Status do Serviço</xs:documentation>
        </xs:annotation>
        <xs:sequence>
            <xs:element name="tpAmb" type="TAmb">
                <xs:annotation>
                    <xs:documentation>Identificação do Ambiente:
                        1 - Produção
                        2 - Homologação
                    </xs:documentation>
                </xs:annotation>
            </xs:element>
            <xs:element name="cUF" type="TCodUfIBGE">
                <xs:annotation>
                    <xs:documentation>Sigla da UF consultada</xs:documentation>
                </xs:annotation>
            </xs:element>
            <xs:element name="xServ">
                <xs:annotation>
                    <xs:documentation>Serviço Solicitado</xs:documentation>
                </xs:annotation>
                <xs:simpleType>
                    <xs:restriction base="TServ">
                        <xs:enumeration value="STATUS"/>
                    </xs:restriction>
                </xs:simpleType>
            </xs:element>
        </xs:sequence>
        <xs:attribute name="versao" type="TVerConsStatServ" use="required"/>
    </xs:complexType>
    <xs:complexType name="TRetConsStatServ">
        <xs:annotation>
            <xs:documentation>Tipo Resultado da Consulta do Status do Serviço</xs:documentation>
        </xs:annotation>
        <xs:sequence>
            <xs:element name="tpAmb" type="TAmb">
                <xs:annotation>
                    <xs:documentation>Identificação do Ambiente:
                        1 - Produção
                        2 - Homologação
                    </xs:documentation>
                </xs:annotation>
            </xs:element>
            <xs:element name="verAplic" type="TVerAplic">
                <xs:annotation>
                    <xs:documentation>Versão do Aplicativo que processou a NF-e</xs:documentation>
                </xs:annotation>
            </xs:element>
            <xs:element name="cStat" type="TStat">
                <xs:annotation>
                    <xs:documentation>Código do status da mensagem enviada.</xs:documentation>
                </xs:annotation>
            </xs:element>
            <xs:element name="xMotivo" type="TMotivo">
                <xs:annotation>
                    <xs:documentation>Descrição literal do status do serviço solicitado.</xs:documentation>
                </xs:annotation>
            </xs:element>
            <xs:element name="cUF" type="TCodUfIBGE">
                <xs:annotation>
                    <xs:documentation>Código da UF responsável pelo serviço</xs:documentation>
                </xs:annotation>
            </xs:element>
            <xs:element name="dhRecbto" type="xs:dateTime">
                <xs:annotation>
                    <xs:documentation>AAAA-MM-DDTHH:MM:SS</xs:documentation>
                </xs:annotation>
            </xs:element>
            <xs:element name="tMed" type="TMed" minOccurs="0">
                <xs:annotation>
                    <xs:documentation>Tempo médio de resposta do serviço (em segundos) dos últimos 5 minutos
                    </xs:documentation>
                </xs:annotation>
            </xs:element>
            <xs:element name="dhRetorno" type="xs:dateTime" minOccurs="0">
                <xs:annotation>
                    <xs:documentation>AAAA-MM-DDTHH:MM:SSDeve ser preenchida com data e hora previstas para o retorno
                        dos serviços prestados.
                    </xs:documentation>
                </xs:annotation>
            </xs:element>
            <xs:element name="xObs" type="TMotivo" minOccurs="0">
                <xs:annotation>
                    <xs:documentation>Campo observação utilizado para incluir informações ao contribuinte
                    </xs:documentation>
                </xs:annotation>
            </xs:element>
        </xs:sequence>
        <xs:attribute name="versao" type="TVerConsStatServ" use="required"/>
    </xs:complexType>
    <xs:simpleType name="TVerConsStatServ">
        <xs:annotation>
            <xs:documentation>Tipo versão do leiuate da Consulta Status do Serviço 2.00</xs:documentation>
        </xs:annotation>
        <xs:restriction base="xs:token">
            <xs:pattern value="2\.00"/>
        </xs:restriction>
    </xs:simpleType>
</xs:schema>

【问题讨论】:

  • 能否发布您的 JAXB 源代码(xsd 或带注释的 Java 类)?

标签: java web-services cxf jaxb2 xmlbeans


【解决方案1】:

您可能希望在生成的类的元素上使用http://docs.oracle.com/javaee/5/api/javax/xml/bind/annotation/XmlElement.html 注释。它具有您会感兴趣的命名空间参数。我们之前遇到过类似的问题,并且能够使其与这些手动更新一起使用。

另见JAXB: How do I annotate classes so that they belong to different namespaces?

【讨论】:

  • Web 服务不接受soap 消息中的任何命名空间前缀。如果消息至少有一个命名空间前缀,则返回被拒绝。
  • @brevleq 这似乎是您调用的服务中的一个非常重要的缺陷。他们是否提供了一个原因,为什么对提供的架构有效的格式良好的 xml 被拒绝?一旦涉及多个模式命名空间,阻止使用前缀可能会导致重复声明适用的命名空间,而不是在更高级别声明 1 次。将其视为始终使用完全限定的 java 类名,而不是在类顶部使用一个 import 语句(这不是一个完美的类比,但很接近)。
  • @brevieq 尝试在 XmlElement 注释上设置“namespace”参数,特别是在 XML 中需要命名空间的每个元素上:在您的情况下为 nfeDadosMsg 和 consStatServ。尝试在 XmlType 注释上设置 namespace="" 。我知道这不是处理它的最佳方法,它是使它在我们的案例中起作用的唯一方法。
【解决方案2】:

我终于解决了这个问题。由于这是第 3 方 Web 服务,我唯一的选择是编辑 JAXB 生成的类。我做的第一件事是从 package-info.java 文件中删除 namespace 属性。然后我在 ObjectFactory 类中做了同样的事情,这里我只是将 QName 构造函数从:

QName(String namespaceURI, String localPart)

QName(String localPart)

很遗憾,这些修改没有去掉前缀,所以我实现了自己的CXF interceptor

public class NamespacePrefixInterceptor extends AbstractPhaseInterceptor<Message> {
    public NamespacePrefixInterceptor() {
        super(Phase.PRE_STREAM);
        addBefore(SoapPreProtocolOutInterceptor.class.getName());
    }

    @Override
    public void handleMessage(Message message) throws Fault {
        if (isOutbound(message)) {
            OutputStream outputStream = message.getContent(OutputStream.class);
            CachedOutputStream cachedOutputStream = new CachedOutputStream();
            message.setContent(OutputStream.class, cachedOutputStream);
            message.getInterceptorChain().doIntercept(message);
            try {
                cachedOutputStream.flush();
                CachedOutputStream messageStream = (CachedOutputStream) message.getContent(OutputStream.class);
                String currentEnvelopeMessage = IOUtils.toString(messageStream.getInputStream(), "UTF-8");
                currentEnvelopeMessage = currentEnvelopeMessage.replaceAll("ns2:", "");
                currentEnvelopeMessage = currentEnvelopeMessage.replaceAll(":ns2", "");
                messageStream.flush();
                messageStream.close();
                InputStream replaceInStream = new ByteArrayInputStream(currentEnvelopeMessage.getBytes(StandardCharsets.UTF_8));
                IOUtils.copy(replaceInStream, outputStream);
                replaceInStream.close();
                outputStream.flush();
                message.setContent(OutputStream.class, outputStream);
                outputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private boolean isOutbound(Message message){
        return message == message.getExchange().getOutMessage()
            || message == message.getExchange().getOutFaultMessage();
    }
}

有了这个拦截器,我终于可以删除命名空间了。最后一个问题是命名空间 xmlns="http://www.portalfiscal.inf.br/nfe" 应该出现在 consStatServ 元素中。由于在修改 JAXB 生成的类后删除了所有不必要的命名空间,我所做的解决方法是在 ConsStatServ 类中创建一个 xmln 属性:

public class ConsStatServ {
    .
    .
    .
    @XmlAttribute(name = "xmlns", required = true)
    @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
    protected String xmlns = "http://www.portalfiscal.inf.br/nfe";
    public String getXmlns() {
        return xmlns;
    }
}

我知道这个解决方案很麻烦,但我找不到其他选择。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-23
    • 1970-01-01
    • 2011-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多