2010-02-12 22 views
19

Tôi có thiết lập JAXB khi tôi sử dụng @XmlJavaTypeAdapter để thay thế các đối tượng thuộc loại Person với các đối tượng thuộc loại PersonRef chỉ chứa UUID của người đó. Điều này hoạt động hoàn toàn tốt đẹp. Tuy nhiên, XML được tạo ra sẽ tạo lại cùng một không gian tên (xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance") mỗi khi nó được sử dụng. Trong khi điều này nói chung là ổn, nó chỉ cảm thấy không đúng.JAXB: Cách tránh định nghĩa không gian tên lặp lại cho xmlns: xsi

Làm cách nào tôi có thể định cấu hình JAXB để khai báo xmlns: xsi ở phần đầu của tài liệu? Tôi có thể thêm các khai báo vùng tên theo cách thủ công vào phần tử gốc không?

Dưới đây là một ví dụ về những gì tôi muốn đạt được:

hiện tại:

<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a"> 
    <relation type="CHILD"> 
     <to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
    </relation> 
    <relation type="CHILD"> 
     <to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
    </relation> 
    <!-- SNIP: some more relations --> 
</person> 

Wanted:

<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    <relation type="CHILD"> 
     <to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0"/> 
    </relation> 
    <relation type="CHILD"> 
     <to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a"/> 
    </relation> 
    <!-- SNIP: some more relations --> 
</person> 

Trả lời

5

Bạn có thể làm điều đó với các mã:

marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapper() { 
       @Override 
       public String[] getPreDeclaredNamespaceUris() { 
        return new String[] { 
         XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI 
        }; 
       } 

       @Override 
       public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { 
        if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI)) 
         return "xsi"; 
        if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI)) 
         return "xs"; 
        if (namespaceUri.equals(WellKnownNamespace.XML_MIME_URI)) 
         return "xmime"; 
        return suggestion; 

       } 
      }); 
+1

Tôi sử dụng jaxb2, điều này không hiệu quả đối với tôi , ném RuntimeException – arrehman

0

Đó là XML, do đó bạn có thể xử lý đầu ra sử dụng DOM hoặc XSLT để loại bỏ nhiều tham chiếu không gian tên.

+11

Xin lỗi, nhưng tôi thà tự bắn vào chân mình :) – sfussenegger

+0

Nhưng nghiêm túc, ném XSLT hoặc DOM ở nhiệm vụ này có vẻ hơi quyết liệt. Cuối cùng, điều này chỉ đơn giản là làm hài lòng mong muốn của tôi về thẩm mỹ :) – sfussenegger

+0

JAXB không thể cấu hình được. Tôi nghĩ rằng đó là cách duy nhất để làm điều này (hoặc để cho nó). –

9

Nó trông giống như một JAXB customization Namespace mapper issue

Khi bạn marshall một tài liệu XML sử dụng JAXB 1.0, một đối tượng marshaller, một đối tượng JAXB điều khiển quá trình marshalling, cung cấp tờ khai không gian tên trong tài liệu XML kết quả. Đôi khi marshaller sản xuất rất nhiều tờ khai namespace trông dư thừa, ví dụ:

<?xml version="1.0"?> 
    <root> 
     <ns1:element xmlns:ns1="urn:foo"> ... </ns1:element> 
     <ns2:element xmlns:ns2="urn:foo"> ... </ns2:element> 
     <ns3:element xmlns:ns3="urn:foo"> ... </ns3:element> 
    </root> 

JAXB 2.0 thay đổi hành vi này. Nếu bạn sử dụng JAXB 2.0 (hoặc mới hơn) để sắp xếp một tài liệu XML, thì Marshaller khai báo tất cả các không gian tên được biết đến đồng nhất (URI), đó là các URI đó được sử dụng làm tên phần tử hoặc thuộc tính trong chú thích JAXB.

JAXB cũng có thể khai báo không gian tên bổ sung ở giữa tài liệu XML, ví dụ: khi tên đủ điều kiện (QName) được sử dụng làm giá trị thuộc tính hoặc phần tử yêu cầu URI vùng tên mới hoặc khi Mô hình đối tượng tài liệu (DOM) node trong cây nội dung yêu cầu một URI không gian tên mới. Hành vi này có thể tạo ra một tài liệu XML có rất nhiều khai báo không gian tên với các tiền tố không gian tên được tạo tự động.

Vấn đề là các tiền tố không gian tên được tạo tự động như ns1, ns2 và ns3, không thân thiện với người dùng - chúng thường không giúp mọi người hiểu XML được sắp xếp theo thứ tự.

May mắn thay, JAXB 2.0 (hoặc mới hơn) cung cấp giao diện nhà cung cấp dịch vụ (SPI) có tên com.sun.xml.bind.marshaller.NamespacePrefixMapper mà bạn có thể sử dụng để chỉ định tiền tố không gian tên hữu ích hơn cho việc đầm lầy.

Khi chương trình JAXBSample sắp xếp lại tài liệu XML lần đầu tiên, nó thực hiện mà không cần sử dụng lớp NamespacePrefixMapper. Kết quả là, Marshaller tự động tạo tiền tố không gian tên, trong trường hợp này là ns2.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
    <ns2:JustAnElement xmlns:ns2="a"> 
     <foo>true</foo> 
    </ns2:JustAnElement> 

Ví dụ về cấu hình tránh sự lặp lại không gian tên:

Các marshalling thứ hai thực hiện bởi các chương trình JAXBSample sử dụng một lớp NamespacePrefixMapper như sau:

NamespacePrefixMapper m = new PreferredMapper(); 
       marshal(jc, e, m); 

    public static class PreferredMapper extends NamespacePrefixMapper { 
      @Override 
      public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { 
       return "mappedNamespace" + namespaceUri; 
      } 
     } 

Phương thứctrong lớp PreferredMapper trả về tiền tố ưa thích, trong trường hợp này, mappedNamespacea được khai báo ở phần tử gốc của XML được trộn lẫn.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
    <mappedNamespacea:JustAnElement xmlns:mappedNamespacea="a"> 
     <foo>true</foo> 
    </mappedNamespacea:JustAnElement> 
+0

Xem thêm http://khylo.blogspot.com/2008/08/jaxb-multiple-namespace-definitions.html về vấn đề không liên quan trực tiếp. – VonC

+0

Cảm ơn câu trả lời của bạn. NamespacePrefixMapper mặc định đã ánh xạ "http://www.w3.org/2001/XMLSchema-instance" tới "xsi" (xem http://j.mp/dkCcgC). Vì vậy, tôi không nghĩ rằng có một cách để khai báo các không gian tên trước đó bằng cách sử dụng NamespacePrefixMapper. NamespaceContext trông đầy hứa hẹn. Tôi vẫn cố gắng tìm hiểu làm thế nào để có được bàn tay của tôi trên nó mặc dù. (Dù sao +1 cho nỗ lực của bạn) – sfussenegger

+0

Trong trường hợp bạn quan tâm: 3 tháng sau, hóa ra câu trả lời của bạn không quá xa: đúng lớp, phương pháp sai (xem câu trả lời của Dany). Tôi không thể tin rằng tôi đã không lưu ý phương thức getPreDeclaredNamespaceUris() trong khi kiểm tra đề xuất của bạn. – sfussenegger

16

Không rằng đẹp, nhưng bạn có thể thêm một schemaLocation trống để phần tử gốc:

marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, ""); 
+0

thực sự không * mà * đẹp :) nhưng ít nhất nó gần với một câu trả lời đúng. – sfussenegger

+0

Tôi nghĩ nó tuyệt đẹp! Tôi buộc phải ở lại trên một thời gian chạy Java 6 với JAXB 2.x. Với sự thay đổi của bạn, tôi chỉ nhận khai báo lược đồ trên phần tử gốc, chứ không phải trên bất kỳ phần tử con nào. Phần tử gốc trông giống như sau: '' –

3

nếu bạn đang sử dụng Maven sau đó chỉ cần thêm vào pom của bạn:

<dependency> 
      <groupId>com.sun.xml.bind</groupId> 
      <artifactId>jaxb-impl</artifactId> 
      <version>2.2.2</version> 
      <type>jar</type> 
      <scope>compile</scope> 
     </dependency> 

không cần PreferredMapper nếu bạn định cấu hình chú thích của mình như được xác định trong ví dụ ở trên. Mặc dù tôi có một tệp confugures gói-info.jave như sau:

@javax.xml.bind.annotation.XmlSchema(
     namespace = "mylovelynamespace1", 
     xmlns = { 
        @javax.xml.bind.annotation.XmlNs(prefix = "myns1", namespaceURI = "mylovelynamespace1"), 
        @javax.xml.bind.annotation.XmlNs(prefix = "myns2", namespaceURI = "mylovelynamespace2") 
       }, 
       elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) 
package com.mylovelycompanyname.package; 
2

Đây là câu trả lời hay nhất tôi tìm thấy trong web.

Tuyên bố xsi:type rất có thể được tạo vì loại khai báo của JAXBElement không khớp với loại giá trị.

Nếu ObjectFactory có phương pháp tạo cho đúng JAXBElement, bạn nên sử dụng phương pháp này vì phương pháp này phải điền chính xác cả QName và thông tin loại; nếu không, tôi sẽ thử đặt kiểu khai báo (hàm tạo thứ hai arg) của số JAXBElement thành String.class (giả sử đây là loại commentTest) thay vì CommentType.Comment.

Nguồn: http://www.java.net/forum/topic/glassfish/metro-and-jaxb/how-do-i-remove-namespace-declarations-child-elements

Chủ đầu tư: cbrettin

1

Bạn có thể để không gian tên được viết chỉ một lần. Bạn sẽ cần một lớp proxy của XMLStreamWriter và một package-info.java. Sau đó, bạn sẽ làm gì trong mã của bạn:

StringWriter stringWriter = new StringWriter(); 
XMLStreamWriter writer = new Wrapper((XMLStreamWriter) XMLOutputFactory 
                   .newInstance().createXMLStreamWriter(stringWriter)); 
JAXBContext jaxbContext = JAXBContext.newInstance(Collection.class); 
Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); 
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); 
jaxbMarshaller.marshal(books, writer); 
System.out.println(stringWriter.toString()); 

lớp Proxy (phương pháp quan trọng là "writeNamespace"):

  class WrapperXMLStreamWriter implements XMLStreamWriter { 

        private final XMLStreamWriter writer; 

        public WrapperXMLStreamWriter(XMLStreamWriter writer) { 
         this.writer = writer; 
        } 

        //keeps track of what namespaces were used so that not to 
        //write them more than once 
        private List<String> namespaces = new ArrayList<String>(); 

        public void init(){ 
         namespaces.clear(); 
        } 

        public void writeStartElement(String localName) throws XMLStreamException { 
         init(); 
         writer.writeStartElement(localName); 

        } 

        public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException { 
         init(); 
         writer.writeStartElement(namespaceURI, localName); 
        } 

        public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException { 
         init(); 
         writer.writeStartElement(prefix, localName, namespaceURI); 
        } 

        public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException { 
         if(namespaces.contains(namespaceURI)){ 
          return; 
         } 
         namespaces.add(namespaceURI); 
         writer.writeNamespace(prefix, namespaceURI); 
        } 

    // .. other delegation method, always the same pattern: writer.method() ... 

} 

gói-info.java:

@XmlSchema(elementFormDefault=XmlNsForm.QUALIFIED, attributeFormDefault=XmlNsForm.UNQUALIFIED , 
     xmlns = { 
     @XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi")}) 
package your.package; 

import javax.xml.bind.annotation.XmlNs; 
import javax.xml.bind.annotation.XmlNsForm; 
import javax.xml.bind.annotation.XmlSchema; 
0

Thêm ánh xạ nsPrefix của bạn bằng cách làm này:

marshaller.setNamespaceMapping("myns","urn:foo");

Các vấn đề liên quan