2011-08-25 22 views
8

Tôi có một file XML:JAXB: Làm thế nào tôi có thể unmarshal XML mà không gian tên

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<object> 
    <str>the type</str> 
    <bool type="boolean">true</bool>   
</object> 

Và tôi muốn unmarshal nó đến một đối tượng của lớp dưới

@XmlRootElement(name="object") 
public class Spec { 
    public String str; 
    public Object bool; 

} 

Làm thế nào tôi có thể làm điều này ? Trừ khi tôi chỉ định các không gian tên (xem bên dưới), nó không hoạt động.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<object> 
    <str>the type</str> 
    <bool xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     xsi:type="xs:boolean">true</bool>   
</object> 

Trả lời

5

CẬP NHẬT

Bạn có thể có được điều này để làm việc bằng cách giới thiệu một lớp trung gian để dịch giữa typexsi:type. Dưới đây là ví dụ về cách sử dụng Stax StreamReaderDelegate làm điều này cho các hoạt động unmarshal JAXB:

package forum7184526; 

import java.io.FileInputStream; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.stream.XMLInputFactory; 
import javax.xml.stream.XMLStreamReader; 
import javax.xml.stream.util.StreamReaderDelegate; 

import org.eclipse.persistence.oxm.XMLConstants; 

public class Demo { 

    public static void main(String[] args) throws Exception { 
     XMLInputFactory xif = XMLInputFactory.newFactory(); 
     XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("input.xml")); 
     xsr = new XsiTypeReader(xsr); 

     JAXBContext jc = JAXBContext.newInstance(Spec.class); 
     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     Spec spec = (Spec) unmarshaller.unmarshal(xsr); 
    } 

    private static class XsiTypeReader extends StreamReaderDelegate { 

     public XsiTypeReader(XMLStreamReader reader) { 
      super(reader); 
     } 

     @Override 
     public String getAttributeNamespace(int arg0) { 
      if("type".equals(getAttributeLocalName(arg0))) { 
       return XMLConstants.SCHEMA_INSTANCE_URL; 
      } 
      return super.getAttributeNamespace(arg0); 
     } 

    } 
} 

xsi:type là một cơ chế đồ để xác định loại thực của một phần tử (tương tự như một dàn diễn viên trong Java). Nếu bạn loại bỏ không gian tên, bạn đang thay đổi ngữ nghĩa của tài liệu.

Trong EclipseLink JAXB (MOXy) chúng tôi cho phép bạn xác định chỉ số thừa kế của riêng bạn cho các đối tượng sử dụng miền @XmlDescriminatorNode@XmlDescrimatorValue. Hiện tại chúng tôi không cung cấp kiểu tùy chỉnh này cho thuộc tính kiểu dữ liệu:

+0

Cảm ơn phản hồi của bạn, Blaise. Tôi ổn với việc có các không gian tên. Nhưng có thể không có không gian tên trong XML (vì nó sẽ đối mặt với người dùng dev) nhưng giữ thông tin đó với chú thích lớp Java, để khi jaxb phân tích cú pháp XML, nó thấy "type" và biết "aha, nó là của không gian tên 'xsi' theo khai báo lớp Java ". Tương tự với "xs: boolean". Bạn có thấy tôi đang nói gì không? Hoặc nó vẫn không thể? Tôi cần điều này vì người dùng dev của chúng ta sẽ tạo ra các khai báo XML như vậy và chúng ta cần phải điều chỉnh chúng thành Java. – Andrey

+0

@Andrey - Bạn có thể thực hiện công việc này, dưới đây là một cách tiếp cận bạn có thể làm cho sự so sánh. –

+0

Xin chào Blaise. Việc loại bỏ một không gian tên cho thuộc tính "type" đã làm việc tuyệt vời, THANKS A LOT !!!, nhưng làm thế nào tôi có thể loại bỏ khai báo không gian tên "xs" cho giá trị của thuộc tính "type" - "xs: boolean". Tôi đã chơi với việc ghi đè một số phương thức StreamReaderDelegate, nhưng không thành công. – Andrey

2

Dựa trên ý kiến ​​của Blaise (nhờ Blaise!) Và nghiên cứu của tôi. Đây là giải pháp cho vấn đề của tôi. Bạn có đồng ý với Blaise đó không, hoặc bạn có cách nào tốt hơn?

package forum7184526; 

import java.io.FileInputStream; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.stream.XMLInputFactory; 
import javax.xml.stream.XMLStreamReader; 
import javax.xml.stream.util.StreamReaderDelegate; 

import org.eclipse.persistence.oxm.XMLConstants; 

public class Demo { 

public static void main(String[] args) throws Exception { 
    XMLInputFactory xif = XMLInputFactory.newFactory(); 
    XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("input.xml")); 
    xsr = new XsiTypeReader(xsr); 

    JAXBContext jc = JAXBContext.newInstance(Spec.class); 
    Unmarshaller unmarshaller = jc.createUnmarshaller(); 
    Spec spec = (Spec) unmarshaller.unmarshal(xsr); 
} 

private static class XsiTypeReader extends StreamReaderDelegate { 

    public XsiTypeReader(XMLStreamReader reader) { 
     super(reader); 
    } 

    @Override 
    public String getAttributeNamespace(int arg0) { 
     if("type".equals(getAttributeLocalName(arg0))) { 
      return "http://www.w3.org/2001/XMLSchema-instance"; 
     } 
     return super.getAttributeNamespace(arg0); 
    } 

    @Override 
    public String getAttributeValue(int arg0) { 
     String n = getAttributeLocalName(arg0); 
     if("type".equals(n)) { 
     String v = super.getAttributeValue(arg0); 
      return "xs:"+ v; 
     } 
     return super.getAttributeValue(arg0); 
    } 
    @Override 
    public NamespaceContext getNamespaceContext() { 
     return new MyNamespaceContext(super.getNamespaceContext()); 
    } 
} 

private static class MyNamespaceContext implements NamespaceContext { 
    public NamespaceContext _context; 
    public MyNamespaceContext(NamespaceContext c){ 
    _context = c; 
    } 

    @Override 
    public Iterator<?> getPrefixes(String namespaceURI) { 
    return _context.getPrefixes(namespaceURI); 
    } 

    @Override 
    public String getPrefix(String namespaceURI) { 
    return _context.getPrefix(namespaceURI); 
    } 

    @Override 
    public String getNamespaceURI(String prefix) { 
    if("xs".equals(prefix)) { 
     return "http://www.w3.org/2001/XMLSchema"; 
    } 
    return _context.getNamespaceURI(prefix); 
    } 
} 
} 
7

Cách dễ dàng hơn có thể là sử dụng unmarshalByDeclaredType, vì bạn đã biết loại bạn muốn đối sánh.

Bằng cách sử dụng

Unmarshaller.unmarshal(rootNode, MyType.class); 

bạn không cần phải có một tuyên bố namespace trong XML, kể từ khi bạn vượt qua trong các JAXBElement có không gian tên đã được thiết lập.

Điều này cũng hoàn toàn hợp pháp, vì bạn không bắt buộc phải tham chiếu không gian tên trong một cá thể XML, xem http://www.w3.org/TR/xmlschema-0/#PO - và nhiều khách hàng tạo XML theo cách đó.

Cuối cùng, nó đã hoạt động.Lưu ý rằng bạn phải loại bỏ bất kỳ không gian tên tùy chỉnh nào trong lược đồ; ở đây làm việc mẫu mã:

Giản đồ:

<xsd:schema 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
<xsd:element name="customer"> 
    <xsd:complexType> 
     <xsd:sequence minOccurs="1" maxOccurs="1"> 
     <xsd:element name="name" type="xsd:string" minOccurs="1" maxOccurs="1" /> 
     <xsd:element name="phone" type="xsd:string" minOccurs="1" maxOccurs="1" /> 
     </xsd:sequence> 
    </xsd:complexType> 
</xsd:element> 

XML:

<?xml version="1.0" encoding="UTF-8"?> 
<customer> 
    <name>Jane Doe</name> 
    <phone>08154712</phone> 
</customer> 

mã JAXB:

JAXBContext jc = JAXBContext.newInstance(Customer.class); 
Unmarshaller u = jc.createUnmarshaller(); 
u.setSchema(schemaInputStream); // load your schema from File or any streamsource 
Customer = u.unmarshal(new StreamSource(inputStream), clazz); // pass in your XML as inputStream 
Các vấn đề liên quan