2012-03-06 26 views
9

Tôi có một hệ thống phân cấp phức tạp của các giao diện Java mà tôi muốn sắp xếp (và không nhất thiết là không đối sánh) với JAXB. Các giao diện này đại diện cho các đối tượng sẽ được trả về từ một API REST JAX-RS như XML, JSON, YAML, v.v. (Tôi đang sử dụng RestEasy, có thể sắp xếp các loại chú thích JAXB theo các định dạng khác với XML.)JAXB marshalling hoàn toàn từ các giao diện

vấn đề có vẻ là JAXB về cơ bản là theo định hướng lớp. Tôi đã thực hiện rất nhiều nghiên cứu về những khó khăn với JAXB và giao diện, các giải pháp gần nhất là MOXy JAXB - Map Interfaces to XMLJAXB and Interface Fronted Models. Tuy nhiên, tôi có hai vấn đề chính: a) Tôi muốn chú thích/làm việc về mặt giao diện, không phải các lớp cụ thể (trong đó sẽ có nhiều triển khai và có chứa trạng thái quan trọng khác không được sắp xếp), và b) tôi có nhiều cấp độ thừa kế giao diện. Dưới đây là một ví dụ về các giao diện, trừ đi bất kỳ JAXB chú thích sao cho đến nay:

interface Uuided { 
    UUID getId(); 
} 
interface Named { 
    String getName(); 
} 
interface Component extends Uuided, Named { 
    Map<String, ComponentAttribute> getAttributes(); 
} 
interface Attribute extends Named { 
    Type getType(); 
    Object getValue(); 
} 
interface ComponentAttribute extends Attribute { 
    Component getDeclaringComponent(); 
} 

Lý tưởng nhất, điều này sẽ tạo ra một cái gì đó như:

<component id="xxx" name="thing"> 
    <attributes> 
    <componentAttribute name="color"> 
     <type><stringType/></type> 
     <value>green</value> 
     <declaringComponent idref="xxx"/> 
    </componentAttribute> 
    </attributes> 
</component> 

Rõ ràng, trong trừu tượng, điều này dẫn đến các vấn đề như xác định nhất giao diện có chú thích bắt nguồn, trong đó về lý thuyết có thể có nhiều hơn một. Tuy nhiên, trong trường hợp của tôi, tôi tin rằng các lớp cụ thể chỉ thực hiện một giao diện duy nhất cần được sắp xếp. Unmarshaling là không cần thiết, vì tôi có các lớp riêng biệt xác định các thuộc tính upsert.

Vì vậy, câu hỏi của tôi là, điều này thậm chí có thể xử lý được với JAXB hay không, và nếu có thì làm cách nào? Ngay cả khi tôi phải rất rõ ràng trong việc xác định các ràng buộc, bộ điều hợp, v.v., tôi muốn làm việc trong khung công tác JAXB để có được lợi ích của tất cả các nhà cung cấp không phải XML trong RestEasy.

+2

Thảo luận liên quan: http://blog.bdoughan.com/2010/07/moxy-jaxb-map-interfaces-to-xml.html?showComment=1331064265196#c8558952364670027314 –

+0

Bạn sẽ gặp sự cố với chú thích giao diện với JAXB , xem [http://stackoverflow.com/questions/4745798/why-java-classes-do-not-inherit-annotations-from-implemented-interfaces](http://stackoverflow.com/questions/4745798/why- java-classes-do-not-inherit-annotations-từ-triển khai-giao diện) –

Trả lời

0

Tôi nghĩ câu trả lời là JAXB không hề có ý định hỗ trợ điều này và thật ngu ngốc khi cố gắng ép buộc. Ngoài ra, marshaling JSON do JAXB điều khiển hóa ra cũng không lý tưởng.

tôi đã kết thúc bằng văn bản khuôn khổ marshaling của riêng tôi, với các thiết lập riêng của mình chú thích:

@MarshalMixin // marshal fields but not a top-level object 
interface Uuided { 
    @MarshalAsString // ignore properties; just call toString() 
    @MarshalId // treat as identifier for @MarshalUsingIds or cyclic ref 
    UUID getId(); 
} 
@MarshalMixin 
interface Named { 
    @MarshalId 
    String getName(); 
} 
@MarshalObject // top-level marshaled object providing class name 
interface Component extends Uuided, Named { 
    @MarshalAsKeyedObjectMap(key = "name") // see description below 
    Map<String, ComponentAttribute> getAttributes(); 
} 
@MarshalObject 
interface Attribute extends Named { 
    Type getType(); 
    @MarshalDynamic // use run-time (not declared) type 
    Object getValue(); 
} 
interface ComponentAttribute extends Attribute { 
    @MarshalUsingIds 
    Component getDeclaringComponent(); 
} 

Các marshalers tạo ra viết thư cho một lớp trừu tượng (hiện triển khai cho JSON và XML). Điều này mang lại sự linh hoạt hơn nhiều để làm cho đầu ra tự nhiên cho các biểu diễn khác nhau mà không cần một tấn chú thích và bộ điều hợp. Ví dụ: những gì tôi gọi là bản đồ đối tượng có khóa, trong đó mỗi đối tượng chứa khóa bản đồ của nó. Trong JSON, bạn muốn có một bản đồ, nhưng trong XML, bạn muốn có một trình tự:

{..., map: {'a': {'name': 'a', ...}, 'b': {'name: 'b', ...}, ...}, ...} 
...<map><thing name='a'>...</thing><thing name='b'>...</thing></map>... 

Có vẻ như càng nhiều càng 4 người khác quan tâm về vấn đề này quá, vì vậy hy vọng tôi có thể mở nguồn nó cuối cùng. :-)

+0

Bạn có thể khái quát hóa nó hơn nữa để hỗ trợ IDL :) Tại sao bạn chọn để dính vào giao diện ở nơi đầu tiên? –

+1

Tôi đã không yêu cầu tổng quát bất cứ điều gì; Tôi chủ yếu tách marshaling khỏi những lo ngại của unmarshaling. Bạn nói đùa, nhưng có lẽ những gì tôi muốn thực sự là một IDL tốt; Tôi chỉ không biết về một khuôn khổ IDL mà ánh xạ tốt và dễ dàng giữa các giao diện Java (không phải các lớp đối tượng dữ liệu!), JSON và XML. Tôi cần giao diện vì việc triển khai của tôi phải đối phó với biểu đồ đối tượng chỉ được giải quyết một phần. Họ ẩn thực tế là các tham chiếu đối tượng được tải từ một cơ sở dữ liệu theo yêu cầu. –

+0

Có vẻ như tùy chọn này của Apache Xerces http: // apache hay không.org/xml/features/dom/defer-node-mở rộng? –

1

Câu trả lời ngắn: sử dụng @XmlElement(type = Object.class) trên trường giao diện của bạn.

chi tiết dưới đây:

tôi đã tìm thấy 2 cách mà bạn có thể làm cho JAXB serialize giao diện của bạn:

  1. @XmlAnyElement
  2. @XmlElement(type = Object.class)

[email protected]

Chỉ cần chú thích trường loại giao diện của bạn với @XmlAnyElement và JAXB sẽ tuần tự hóa giao diện từ loại bê tông của nó. Đừng quên chú thích các loại cụ thể với @XmlRootElement và thêm các loại cụ thể vào JAXBContext. Full ví dụ sau:

public class InterfaceSerializer { 

    @XmlRootElement 
    public static class Pojo { 
     Pojo() { 
      field1 = new PojoFieldImpl1(); 
      field2 = new PojoFieldImpl2(); 
      field3 = new PojoFieldImpl1(); 
     } 

     @XmlAnyElement 
     public IPojoField field1; 
     @XmlAnyElement 
     public IPojoField field2; 
     @XmlAnyElement 
     public IPojoField field3; 

     @Override 
     public String toString() { 
      return "field1 = " + field1 + "\nfield2 = " + field2 + "\nfield3 = " + field3; 
     } 
    } 

    public static interface IPojoField { 

    } 

    @XmlRootElement 
    public static class PojoFieldImpl1 implements IPojoField { 

     PojoFieldImpl1() { 
      value = "PojoFieldImpl1 value"; 
     } 

     public String value; 

     @Override 
     public String toString() { 
      return value; 
     } 
    } 

    @XmlRootElement 
    public static class PojoFieldImpl2 implements IPojoField { 

     PojoFieldImpl2() { 
      value = "PojoFieldImpl2 value1"; 
      value2 = "PojoFieldImpl2 value2"; 
     } 

     public String value; 
     public String value2; 

     @Override 
     public String toString() { 
      return value + " " + value2; 
     } 
    } 

    public static void main(String []args) throws JAXBException { 
     Pojo pojo = new Pojo(); 
     JAXBContext jaxbContext = JAXBContext.newInstance(Pojo.class, PojoFieldImpl1.class, PojoFieldImpl2.class); 
     Marshaller marshaller = jaxbContext.createMarshaller(); 

     marshaller.marshal(pojo, new File("interfaceSerializer.xml")); 
    } 
} 

Output XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<pojo> 
    <pojoFieldImpl1> 
     <value>PojoFieldImpl1 value</value> 
    </pojoFieldImpl1> 
    <pojoFieldImpl2> 
     <value>PojoFieldImpl2 value1</value> 
     <value2>PojoFieldImpl2 value2</value2> 
    </pojoFieldImpl2> 
    <pojoFieldImpl1> 
     <value>PojoFieldImpl1 value</value> 
    </pojoFieldImpl1> 
</pojo> 

Những nhược điểm của phương pháp này:

  • bạn không thể phân biệt từ XML từng lĩnh vực cá nhân từ POJO của bạn (triển khai cùng sẽ được viết cùng một thẻ)
  • bạn không có bất kỳ thông tin loại nào cho Unmarshall XML của bạn (nếu bạn muốn làm như vậy)

Những nhược điểm cố định trong dung dịch thứ hai:

2. @ XmlElement (type = Object.class)

tôi đã vấp phải điều này trong mikesir87's blog post. Đơn giản chỉ cần thay thế các @XmlAnyElement chú thích từ trên cao với @XmlElement(type = Object.class) Bạn nên có một cái gì đó như thế này trong lớp POJO với phần trên:

@XmlElement(type = Object.class) 
public IPojoField field1; 
@XmlElement(type = Object.class) 
public IPojoField field2; 
@XmlElement(type = Object.class) 
public IPojoField field3; 

Re-chạy ví dụ của chúng tôi, XML kết quả:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<pojo> 
    <field1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="pojoFieldImpl1"> 
     <value>PojoFieldImpl1 value</value> 
    </field1> 
    <field2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="pojoFieldImpl2"> 
     <value>PojoFieldImpl2 value1</value> 
     <value2>PojoFieldImpl2 value2</value2> 
    </field2> 
    <field3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="pojoFieldImpl1"> 
     <value>PojoFieldImpl1 value</value> 
    </field3> 
</pojo> 

Điều này cũng có thể được deserialized:

Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 
Pojo unmarshalledPojo = (Pojo) unmarshaller.unmarshal(new File("interfaceSerializer.xml")); 
System.out.println(unmarshalledPojo); 

Kết quả đầu ra:

field1 = PojoFieldImpl1 value 
field2 = PojoFieldImpl2 value1 PojoFieldImpl2 value2 
field3 = PojoFieldImpl1 value 

Có lẽ một "hackish" giải pháp, nhưng nó được công việc làm.

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