2011-11-22 16 views
5

Tôi đang sử dụng thẻ @XmlID và @XmlIDREF để tham chiếu một đối tượng từ một đối tượng khác. Nó hoạt động tốt trong Java 6 ngay cả với các lớp kế thừa. Mã ví dụ mà tôi đã tạo trông như thế này. class Base thẻ sử dụng:XmlID/XmlIDREF và kế thừa trong phiên bản JAXB mới nhất (Java 7)

@XmlRootElement 
@XmlAccessorType(FIELD) 
public class Module { 
    Module() {} 

    @XmlIDREF 
    private Module other; 

    @XmlID 
    private String id; 

    public Module(String id, Module other) { 
     this.id = id; 
     this.other = other; 
    } 
} 

thừa kế lớp:

container cho các lớp này:

@XmlRootElement 
public class Script { 
    Script() {} 
    public Script(Collection<Module> modules) { 
     this.modules = modules; 
    } 

    @XmlElementWrapper 
    @XmlElementRef 
    Collection<Module> modules = new ArrayList<Module>(); 
} 

Khi chạy mã ví dụ này:

public class JaxbTest { 

    private Script createScript() { 
     Module m1 = new Module("Module1", null); 
     Module m2 = new TheModule("Module2", m1, "featured module"); 
     Module m3 = new Module("Module3", m2); 
     return new Script(Arrays.asList(m1, m2, m3)); 
    } 

    private String marshal(Script script) throws Exception { 
     JAXBContext context = JAXBContext.newInstance(Module.class, Script.class, TheModule.class); 
     Writer writer = new StringWriter(); 
     context.createMarshaller().marshal(script, writer); 
     return writer.toString(); 
    } 

    private void runTest() throws Exception { 
     Script script = createScript(); 

     System.out.println(marshal(script)); 
    } 

    public static void main(String[] args) throws Exception { 
     new JaxbTest().runTest(); 
    } 
} 

tôi nhận được XML, trong Java 6:

<script> 
    <modules> 
    <module> 
     <id>Module1</id> 
    </module> 
    <theModule> 
     <other>Module1</other> 
     <id>Module2</id> 
     <feature>featured module</feature> 
    </theModule> 
    <module> 
     <other>Module2</other> 
     <id>Module3</id> 
    </module> 
    </modules> 
</script> 

Lưu ý rằng tham chiếu đến m2 (Ví dụ TheModule) được đăng theo mong đợi. Nhưng khi cùng mã đang chạy dưới Java 7 (JAXB 2.2.4-1) Tôi nhận:

<script> 
    <modules> 
    <module> 
     <id>Module1</id> 
    </module> 
    <theModule> 
     <other>Module1</other> 
     <id>Module2</id> 
     <feature>featured module</feature> 
    </theModule> 
    <module> 
     <other xsi:type="theModule" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
     <other>Module1</other> 
     <id>Module2</id> 
     <feature>featured module</feature> 
     </other> 
     <id>Module3</id> 
    </module> 
    </modules> 
</script> 

Vì vậy, bạn có thể thấy rằng trên mới nhất JAXB @XmlIDREF cho module thừa hưởng không hoạt động!

Trả lời

2

Câu trả lời này là sai. Thay vì dựa vào nó, đọc tài liệu cho JAXBContext.newInstance(...), xem this answer và các nhận xét bên dưới.


Tôi nghĩ bạn nhầm lẫn JAXB với dòng sau.

JAXBContext.newInstance(Module.class, Script.class, TheModule.class); 

Bạn nói với nó rằng bạn muốn serialize các loại XML Script, ModuleTheModule. JAXB sẽ xử lý các đối tượng thuộc loại sau này theo cách đặc biệt, bởi vì bạn đã cung cấp lớp cơ sở của nó: nó thêm một thuộc tính phân biệt đối xử vào nó. Bằng cách này, hai loại này có thể được phân biệt trong XML được tuần tự hóa.

Chỉ thử cung cấp ScriptModule, lớp cơ sở cho tất cả mô-đun.

JAXBContext.newInstance(Module.class, Script.class); 

Thực tế, bạn có thể bỏ hoàn toàn Module hoàn toàn. JAXB sẽ suy ra các kiểu trong đối tượng Script mà bạn cố gắng tuần tự hóa từ ngữ cảnh.

Hành vi này theo cách này không phải là chính xác liên quan đến Java 6. Nó liên quan đến việc triển khai JAXB đang được sử dụng (okay-okay, gần như cùng một điều, tôi biết). Trong các dự án của tôi, tôi sử dụng JAXB 2.2.4-1, trong đó tái tạo vấn đề trong tay trong Java 6 và 7.

Ồ, và một điều nữa: thay vì tạo StringWriter và ghép các đối tượng vào đó, sau đó gửi nội dung của nó đến System.out, bạn có thể sử dụng phần sau để gửi định dạng XML tới stdout.

Marshaller marshaller = context.createMarshaller(); 
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); 
marshaller.marshal(script, System.out); 

Có thể điều này có thể dễ dàng hơn (một chút xíu).

+0

Cảm ơn bạn đã trả lời của bạn. Tôi có một số nghi ngờ. Javadoc cho JAXBContext.newInstance (Class ...) nói rằng người gọi phải liệt kê các lớp cấp cao nhất. "bối cảnh mới sẽ nhận ra tất cả các lớp được chỉ định, nhưng nó cũng sẽ nhận ra bất kỳ lớp nào được tham chiếu trực tiếp/gián tiếp ** tĩnh ** (sic!) từ các lớp được chỉ định. Các lớp con của các lớp được tham chiếu cũng như các lớp tham chiếu @XmlTransient không được đăng ký với JAXBContext. ". Nếu chúng tôi áp dụng tuyên bố này vào ví dụ của Konstantin, lớp con cháu TheModule sẽ không được tự động phát hiện. \ –

+0

@AndyMalakov Có vẻ như bạn đã đúng. Tôi không nhớ chính xác nếu tôi đã đọc tài liệu cho 'newInstance' trở lại sau đó khi tôi trả lời câu hỏi, nhưng tôi nhớ rằng tôi đã thử một số mã và những gì tôi đã viết dường như để kiểm tra. Vì OP không trả lời câu trả lời của tôi bằng bất cứ cách nào tôi đã quên hoàn toàn điều này. Trên thực tế tôi có thể thấy rằng tôi đã hiểu sai câu hỏi/vấn đề thực sự để câu trả lời của tôi hoàn toàn tắt. Tôi sẽ cập nhật câu trả lời của tôi để phản ánh điều này. Cảm ơn các đầu vào! –

0

Điều này có vẻ là lỗi đã biết trong JAXB. Tôi đã có cùng một vấn đề và giải quyết nó bằng cách không sử dụng XmlIDRef, nhưng thay vì sử dụng postDeserialize với một ID tùy chỉnh.

Dưới đây là một báo cáo lỗi:

http://java.net/jira/browse/JAXB-870

tôi không thể sử dụng đề xuất Kohányi của Robert. Điều đó có phù hợp với bạn không?

0

Nếu bạn vẫn bị kẹt với Java7 vào năm 2017, thì có một cách dễ dàng để vượt qua điều này. Vấn đề là khi nối tiếp với một XmlREFId, Jaxb không thể đối phó với các lớp con. Giải pháp là sử dụng bộ điều hợp và trả về phiên bản mới của lớp cơ sở có cùng id với trường hợp đang được sắp xếp (chỉ có id sẽ được sử dụng, vì vậy chúng tôi không cần phải bận tâm đến các trường khác):

public class ModuleXmlAdapter extends XmlAdapter<Module, Module> { 
    @Override 
    public Module unmarshal(Module v) { 
     // there is no problem with deserializing - return as is 
     return v; 
    } 

    @Override 
    public Module marshal(Module v) { 
     if (v == null) { 
      return null; 
     } 
     // here is the trick: 
     return new Module(v.getId(), null); 
    } 
} 

Sau đó, chỉ cần sử dụng bộ chuyển đổi mà bất cứ nơi nào cần thiết:

public class Module { 
    @XmlIDREF 
    @XmlJavaTypeAdapter(ModuleXmlAdapter.class) 
    private Module other; 

    //... 
} 
Các vấn đề liên quan