繁体   English   中英

如何使JAXB正确生成XML?

[英]How make JAXB generate XML correctly?

我正在尝试使用在soap请求中使用两个名称空间的Web服务,但是它不接受任何前缀。 我正在使用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正确生成消息?

更新

这是Web服务的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>

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

另请参见JAXB:如何注释类,使它们属于不同的名称空间?

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

QName(String namespaceURI,String localPart)

QName(String localPart)

不幸的是,这些修改并没有删除前缀,因此我实现了自己的CXF拦截器

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;
    }
}

我知道这个解决方案是一个巨大的难题,但是我找不到其他选择。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM