2010-06-07 14 views
28

Có thể sử dụng JAXB để hủy hợp nhất xml thành một lớp Java cụ thể dựa trên thuộc tính của xml không?Java/JAXB: Unmarshall Xml với lớp con cụ thể dựa trên thuộc tính

<shapes> 
    <shape type="square" points="4" square-specific-attribute="foo" /> 
    <shape type="triangle" points="3" triangle-specific-attribute="bar" /> 
</shapes> 

Tôi muốn có một danh sách các đối tượng Shape chứa một hình tam giác và hình vuông, mỗi thuộc tính hình dạng cụ thể của riêng mình. IE:

abstract class Shape { 
    int points; 
    //...etc 
} 

class Square extends Shape { 
    String square-specific-attribute; 
    //...etc 
} 

class Triangle extends Shape { 
    String triangle-specific-attribute; 
    //...etc 
} 

Tôi hiện đang chỉ đưa tất cả thuộc tính vào một lớp "Hình dạng" lớn và nó ít lý tưởng hơn.

Tôi có thể làm việc này nếu các hình dạng được đặt tên đúng là các phần tử xml, nhưng rất tiếc là tôi không có quyền kiểm soát xml mà tôi đang truy xuất.

Cảm ơn!

Trả lời

17

JAXB là một triển khai cụ thể, cụ thể sẽ cung cấp các điểm mở rộng để thực hiện những việc như vậy. Nếu bạn đang sử dụng EclipseLink JAXB (MOXy) bạn có thể sửa đổi các lớp Shape như sau:

import javax.xml.bind.annotation.XmlAttribute; 
import org.eclipse.persistence.oxm.annotations.XmlCustomizer; 

@XmlCustomizer(ShapeCustomizer.class) 
public abstract class Shape { 

    int points; 

    @XmlAttribute 
    public int getPoints() { 
     return points; 
    } 

    public void setPoints(int points) { 
     this.points = points; 
    } 

} 

Sau đó, bằng cách sử dụng MOXY @XMLCustomizer bạn có thể truy cập vào InheritancePolicy và thay đổi lĩnh vực chỉ số lớp từ "@xsi: gõ" để chỉ "gõ" :

import org.eclipse.persistence.config.DescriptorCustomizer; 
import org.eclipse.persistence.descriptors.ClassDescriptor; 

public class ShapeCustomizer implements DescriptorCustomizer { 

    @Override 
    public void customize(ClassDescriptor descriptor) throws Exception { 
     descriptor.getInheritancePolicy().setClassIndicatorFieldName("@type"); 
    } 
} 

bạn sẽ cần phải đảm bảo rằng bạn có nộp jaxb.properties với bạn lớp mô hình (Shape, Square, vv) với các mục sau đây quy định cụ thể việc thực hiện EclipseLink MOXY JAXB:

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory 

Dưới đây là phần còn lại của các lớp mô hình:

Shapes

import java.util.ArrayList; 
import java.util.List; 

import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement 
public class Shapes { 

    private List<Shape> shape = new ArrayList<Shape>();; 

    public List<Shape> getShape() { 
     return shape; 
    } 

    public void setShape(List<Shape> shape) { 
     this.shape = shape; 
    } 

} 

Quảng trường

import javax.xml.bind.annotation.XmlAttribute; 

public class Square extends Shape { 
    private String squareSpecificAttribute; 

    @XmlAttribute(name="square-specific-attribute") 
    public String getSquareSpecificAttribute() { 
     return squareSpecificAttribute; 
    } 

    public void setSquareSpecificAttribute(String s) { 
     this.squareSpecificAttribute = s; 
    } 

} 

Triangle

import javax.xml.bind.annotation.XmlAttribute; 

public class Triangle extends Shape { 
    private String triangleSpecificAttribute; 

    @XmlAttribute(name="triangle-specific-attribute") 
    public String getTriangleSpecificAttribute() { 
     return triangleSpecificAttribute; 
    } 

    public void setTriangleSpecificAttribute(String t) { 
     this.triangleSpecificAttribute = t; 
    } 

} 

Dưới đây là một chương trình demo để kiểm tra xem mọi thứ suôn sẻ :

import java.io.StringReader; 
import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Marshaller; 
import javax.xml.bind.Unmarshaller; 

public class Demo { 

    public static void main(String[] args) throws Exception { 
     JAXBContext jaxbContext = JAXBContext.newInstance(Shapes.class, Triangle.class, Square.class); 

     StringReader xml = new StringReader("<shapes><shape square-specific-attribute='square stuff' type='square'><points>4</points></shape><shape triangle-specific-attribute='triangle stuff' type='triangle'><points>3</points></shape></shapes>"); 
     Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 
     Shapes root = (Shapes) unmarshaller.unmarshal(xml); 

     Marshaller marshaller = jaxbContext.createMarshaller(); 
     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
     marshaller.marshal(root, System.out); 
    } 
} 

Tôi hy vọng điều này sẽ hữu ích.

Để biết thêm thông tin về EclipseLink MOXY xem:

EDIT

Trong EclipseLink 2.2 chúng tôi đang làm cho dễ dàng hơn này để cấu hình, hãy kiểm tra bài viết sau để biết thêm thông tin:

+0

Tôi chắc chắn quan tâm đến điều này như một giải pháp, nhưng tôi dường như không thể làm cho nó hoạt động được. Liệu hình dạng có thực sự không phải là phần tử gốc của tôi không? Nếu tôi cố gắng và unmarshall bộ sưu tập JAXB/MOXy dường như không bận tâm bằng cách sử dụng Customizer và ném một loạt các lỗi về cố gắng để nhanh chóng một lớp trừu tượng. Tôi còn thiếu một phần khác không? – Frothy

+0

Tôi tin rằng bạn đang thiếu tệp jaxb.properties chỉ định EclipseLink MOXy là triển khai JAXB. Tôi đã cập nhật ví dụ trên để hoàn chỉnh hơn. –

+0

Bạn thật tuyệt vời! Điều này hoạt động hoàn hảo. Cảm ơn bạn đã đi xa hơn và hơn thế nữa để giúp tôi với điều này. – Frothy

0

Không, tôi e rằng đó không phải là một lựa chọn, JAXB không phải là linh hoạt.

Điều tốt nhất tôi có thể đề xuất là bạn đặt một phương thức trên lớp Shape để khởi tạo loại "đúng" dựa trên thuộc tính. Mã máy khách sẽ gọi phương thức factory đó để lấy nó.

Tốt nhất tôi có thể nghĩ ra, xin lỗi.

+0

tôi đã không nghĩ đến điều này thực sự. Chắc chắn không phải lý tưởng nhưng nó có thể sẽ hoạt động. Tôi đã thực sự hy vọng cho một số cách rơi vào một adapter tùy chỉnh hoặc một cái gì đó. Cảm ơn đã trả lời nhanh chóng! – Frothy

+0

@Frothy: JAXB thực sự khá hạn chế. Có những lựa chọn thay thế mang lại sự linh hoạt hơn, chẳng hạn như JiBX. – skaffman

+0

Tôi chưa bao giờ nghe nói về JiBX, cảm ơn, tôi sẽ xem xét điều đó vào tối nay. – Frothy

3

AFAIK, bạn sẽ phải viết một số XmlAdapter biết cách xử lý nguyên soái/unmarshalling của Hình dạng.

+0

'XmlAdapter' là để dịch các giá trị chuỗi riêng lẻ thành một loại cụ thể, nó không sử dụng để dịch các loại phức tạp. – skaffman

+0

@Skaffman: XmlAdapter có thể dễ dàng được sử dụng với các loại phức tạp. Một trong những trường hợp sử dụng phổ biến nhất là lập bản đồ java.util.Map cho XML. –

7

Chú thích @XmlElements cho phép bạn xác định những từ khóa tương ứng với đó lớp con.

@XmlElements({ 
    @XmlElement(name="square", type=Square.class), 
    @XmlElement(name="triangle", type=Triangle.class) 
}) 
public List<Shape> getShape() { 
    return shape; 
} 

Xem thêm javadoc cho @XmlElements

+0

sẽ không hoạt động (không có @ quyết định dựa trên logic quyết định) –

-2

Có @XmlSeeAlso chú thích nói để ràng buộc các lớp con.

Ví dụ, với các định nghĩa lớp sau:

class Animal {} 
class Dog extends Animal {} 
class Cat extends Animal {} 

Người dùng sẽ được yêu cầu để tạo JAXBContext như JAXBContext.newInstance (Dog.class, Cat.class) (Animal sẽ được tự động nhặt từ Chó và Cát đề cập đến nó)

XmlSeeAlso chú thích sẽ cho phép bạn viết:.

@XmlSeeAlso({Dog.class,Cat.class}) 
class Animal {} 
class Dog extends Animal {} 
class Cat extends Animal {} 
+1

Làm thế nào để giải quyết vấn đề này? (ánh xạ các lớp khác nhau dựa trên thuộc tính phần tử) –

+0

Tôi đã có một vấn đề rất giống nhau và đây là những gì tôi đã mất tích, cảm ơn – liang