如何避免编组元素/标记中不必要的命名空间声明?

时间:2021-01-07 17:15:10

I have a XSD which is not self created but received from another party. So I can't change this XSD because I must ensure comptibility with the other party.

我有一个XSD,它不是自己创建的,而是从另一方收到的。所以我不能改变这个XSD,因为我必须确保与另一方的兼容性。

Using XJC 2.2 and JAXB 2.2 using simple binding mode I want to create an root element with inside an empty hello element. But when marshalled I get a lot of extra namespace crap. Which to me looks unneeded. (It works though, but it's more data to send ect...)

使用简单绑定模式使用XJC 2.2和JAXB 2.2我想在一个空的hello元素内部创建一个根元素。但是当编组时我得到了很多额外的命名空间废话。对我来说哪个看起来不需要。 (它虽然有效,但发送的数据更多......)

XSD Rootelement:

XSD Rootelement:

<element name="epp">
        <complexType>
            <choice>
                <element name="greeting" type="epp:greetingType" />
                <element name="hello" />
                <element name="command" type="epp:commandType" />
                <element name="response" type="epp:responseType" />
                <element name="extension" type="epp:extAnyType" />
            </choice>
        </complexType>
    </element>

Java code:

Java代码:

Epp epp = new Epp(); 
epp.setHello("");

Marshalled Result:

编组结果:

<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
     <hello xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string"></hello>
</epp>

Preferred Result:

首选结果:

<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<hello />
</epp>

Or:

要么:

<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<hello></hello>
</epp>

Is there any way to make this possible, preferably without changing XSD or manually change the XJC compiled classes?

有没有办法使这成为可能,最好不更改XSD或手动更改XJC编译的类?

2 个解决方案

#1


4  

The problem is as follows: the schema doesn't define a type for element hello. As a result, XJC generates a field with type Object. That means that JAXB must detect, during marshalling, what kind of object we're dealing with. I'm not certain about the details, but I suppose it'll check the runtime type and then deals with it accordingly. Since String - which is what you actually put into the hello field - has a direct binding to a schema type (namely xs:string) JAXB is gonna go with that. So far, so good.

问题如下:架构没有为元素hello定义类型。因此,XJC生成一个Object类型的字段。这意味着JAXB必须在编组期间检测到我们正在处理的是什么类型的对象。我不确定细节,但我想它会检查运行时类型然后相应地处理它。因为String - 你实际放入hello字段的东西 - 直接绑定到一个模式类型(即xs:string)JAXB就是这样。到现在为止还挺好。

But JAXB is trying to generate XML that'll be useful for unmarshalling as well. Since the schema didn't specify a type and the hello field is an Object, trying to unmarshal from XML would leave JAXB guessing as to what it should actually turn the content into. One way to tell it how is to specify the type in the XML element, using the xsi:type attribute. This attribute falls within the xsi-bound namespace, so that prefix must be declared and bound. That's what happens with xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance". But that's not all... The declared xsi:type makes use of a type in the XML Schema namespace, bound to prefix xs which means THAT must be declared as well! Hence the xmlns:xs="http://www.w3.org/2001/XMLSchema".

但是JAXB正在尝试生成对解组也有用的XML。由于模式没有指定类型,并且hello字段是一个Object,因此尝试从XML解组将使JAXB猜测它应该实际将内容转换为什么。告诉它如何使用xsi:type属性指定XML元素中的类型的一种方法。此属性属于xsi-bound命名空间,因此必须声明和绑定前缀。这就是xmlns:xsi =“http://www.w3.org/2001/XMLSchema-instance”。但这并不是全部...声明的xsi:type使用XML Schema命名空间中的类型,绑定到前缀xs,这意味着必须声明它!因此xmlns:xs =“http://www.w3.org/2001/XMLSchema”。

The result: a mess of namespace declarations just to tell whoever uses the XML that it does, in fact, contain a string. This could be solved by adding string as the type for the hello element in the schema, but that's not an option for you.

结果是:一堆命名空间声明只是为了告诉使用XML的人,事实上,它包含一个字符串。这可以通过添加字符串作为模式中hello元素的类型来解决,但这不是一个选项。

Fortunately, you're not totally out of luck. You can customize bindings using an external bindings file. Details can be found here: http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/JAXBUsing4.html

幸运的是,你并没有完全失去运气。您可以使用外部绑定文件自定义绑定。详细信息可以在这里找到:http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/JAXBUsing4.html

Normally, this bindings file ought to do the trick:

通常,这个绑定文件应该做的伎俩:

<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
    version="2.1">

    <!-- Bindings for the general schema -->
    <bindings schemaLocation="test.xsd" node="/xs:schema">

        <bindings node="xs:element[@name='epp']">
            <bindings node=".//xs:element[@name='hello']">
                <javaType name="java.lang.String" />
            </bindings>
        </bindings>

    </bindings>

</bindings>

... but when I try xjc with this I'm getting the error the compiler was unable to honor this javaType customization. It does work when I specify some standard schema type (like string or int) on the hello element in the schema, but again didn't work when I actually tried to supply parse and print methods for the conversion, so I'm gonna have to assume this is a bug in xjc that occurs when no type is specified in the schema.

...但是当我尝试使用xjc时,我收到错误,编译器无法遵守此javaType自定义。当我在模式中的hello元素上指定一些标准模式类型(如string或int)时,它确实有效,但当我实际尝试为转换提供解析和打印方法时再次无法工作,所以我会有假设这是xjc中的一个错误,当架构中没有指定类型时会发生这个错误。

I'm hoping someone else can give advice regarding the bindings issue. Otherwise, the only option I see is sending your schema through some XSLT transform prior to unleashing XJC on it to set every non-typed element to string by default.

我希望其他人可以就绑定问题提出建议。否则,我看到的唯一选择是在释放XJC之前通过某个XSLT转换发送模式,以便默认情况下将每个非类型化元素设置为字符串。

#2


1  

When the schema doesn't specify a type for an element, the default type is xs:anyType, which is the root of the XML Schema type hierarchy (all simple and complex types are subtypes of anyType).

当架构未指定元素的类型时,默认类型为xs:anyType,它是XML架构类型层次结构的根(所有简单类型和复杂类型都是anyType的子类型)。

When JAXB encounters an anyType element it will bind it to a property of type Object. The value you put into this property can be

当JAXB遇到anyType元素时,它会将它绑定到Object类型的属性。您放入此属性的值可以是

  • null, meaning omit the element
  • null,意思是省略元素
  • an object of a type the JAXBContext knows about, which will be marshalled in the normal way and an xsi:type added to indicate what the original type was, or
  • JAXBContext知道的类型的对象,它将以正常方式编组并添加xsi:type以指示原始类型是什么,或者
  • an org.w3c.dom.Element representing the actual XML to use.
  • 表示要使用的实际XML的org.w3c.dom.Element。

So try this:

所以试试这个:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().newDocument();

epp.setHello(doc.createElementNS("urn:ietf:params:xml:ns:epp-1.0", "hello"));

#1


4  

The problem is as follows: the schema doesn't define a type for element hello. As a result, XJC generates a field with type Object. That means that JAXB must detect, during marshalling, what kind of object we're dealing with. I'm not certain about the details, but I suppose it'll check the runtime type and then deals with it accordingly. Since String - which is what you actually put into the hello field - has a direct binding to a schema type (namely xs:string) JAXB is gonna go with that. So far, so good.

问题如下:架构没有为元素hello定义类型。因此,XJC生成一个Object类型的字段。这意味着JAXB必须在编组期间检测到我们正在处理的是什么类型的对象。我不确定细节,但我想它会检查运行时类型然后相应地处理它。因为String - 你实际放入hello字段的东西 - 直接绑定到一个模式类型(即xs:string)JAXB就是这样。到现在为止还挺好。

But JAXB is trying to generate XML that'll be useful for unmarshalling as well. Since the schema didn't specify a type and the hello field is an Object, trying to unmarshal from XML would leave JAXB guessing as to what it should actually turn the content into. One way to tell it how is to specify the type in the XML element, using the xsi:type attribute. This attribute falls within the xsi-bound namespace, so that prefix must be declared and bound. That's what happens with xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance". But that's not all... The declared xsi:type makes use of a type in the XML Schema namespace, bound to prefix xs which means THAT must be declared as well! Hence the xmlns:xs="http://www.w3.org/2001/XMLSchema".

但是JAXB正在尝试生成对解组也有用的XML。由于模式没有指定类型,并且hello字段是一个Object,因此尝试从XML解组将使JAXB猜测它应该实际将内容转换为什么。告诉它如何使用xsi:type属性指定XML元素中的类型的一种方法。此属性属于xsi-bound命名空间,因此必须声明和绑定前缀。这就是xmlns:xsi =“http://www.w3.org/2001/XMLSchema-instance”。但这并不是全部...声明的xsi:type使用XML Schema命名空间中的类型,绑定到前缀xs,这意味着必须声明它!因此xmlns:xs =“http://www.w3.org/2001/XMLSchema”。

The result: a mess of namespace declarations just to tell whoever uses the XML that it does, in fact, contain a string. This could be solved by adding string as the type for the hello element in the schema, but that's not an option for you.

结果是:一堆命名空间声明只是为了告诉使用XML的人,事实上,它包含一个字符串。这可以通过添加字符串作为模式中hello元素的类型来解决,但这不是一个选项。

Fortunately, you're not totally out of luck. You can customize bindings using an external bindings file. Details can be found here: http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/JAXBUsing4.html

幸运的是,你并没有完全失去运气。您可以使用外部绑定文件自定义绑定。详细信息可以在这里找到:http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/JAXBUsing4.html

Normally, this bindings file ought to do the trick:

通常,这个绑定文件应该做的伎俩:

<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
    version="2.1">

    <!-- Bindings for the general schema -->
    <bindings schemaLocation="test.xsd" node="/xs:schema">

        <bindings node="xs:element[@name='epp']">
            <bindings node=".//xs:element[@name='hello']">
                <javaType name="java.lang.String" />
            </bindings>
        </bindings>

    </bindings>

</bindings>

... but when I try xjc with this I'm getting the error the compiler was unable to honor this javaType customization. It does work when I specify some standard schema type (like string or int) on the hello element in the schema, but again didn't work when I actually tried to supply parse and print methods for the conversion, so I'm gonna have to assume this is a bug in xjc that occurs when no type is specified in the schema.

...但是当我尝试使用xjc时,我收到错误,编译器无法遵守此javaType自定义。当我在模式中的hello元素上指定一些标准模式类型(如string或int)时,它确实有效,但当我实际尝试为转换提供解析和打印方法时再次无法工作,所以我会有假设这是xjc中的一个错误,当架构中没有指定类型时会发生这个错误。

I'm hoping someone else can give advice regarding the bindings issue. Otherwise, the only option I see is sending your schema through some XSLT transform prior to unleashing XJC on it to set every non-typed element to string by default.

我希望其他人可以就绑定问题提出建议。否则,我看到的唯一选择是在释放XJC之前通过某个XSLT转换发送模式,以便默认情况下将每个非类型化元素设置为字符串。

#2


1  

When the schema doesn't specify a type for an element, the default type is xs:anyType, which is the root of the XML Schema type hierarchy (all simple and complex types are subtypes of anyType).

当架构未指定元素的类型时,默认类型为xs:anyType,它是XML架构类型层次结构的根(所有简单类型和复杂类型都是anyType的子类型)。

When JAXB encounters an anyType element it will bind it to a property of type Object. The value you put into this property can be

当JAXB遇到anyType元素时,它会将它绑定到Object类型的属性。您放入此属性的值可以是

  • null, meaning omit the element
  • null,意思是省略元素
  • an object of a type the JAXBContext knows about, which will be marshalled in the normal way and an xsi:type added to indicate what the original type was, or
  • JAXBContext知道的类型的对象,它将以正常方式编组并添加xsi:type以指示原始类型是什么,或者
  • an org.w3c.dom.Element representing the actual XML to use.
  • 表示要使用的实际XML的org.w3c.dom.Element。

So try this:

所以试试这个:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().newDocument();

epp.setHello(doc.createElementNS("urn:ietf:params:xml:ns:epp-1.0", "hello"));