2011-03-10 18 views
5

Có cách nào để kể một ObjectOutputStream mà lĩnh vực của một lớp serializable nên được tuần tự mà không sử dụng các từ khóa transient và không xác định một -array serialPersistentFields?Nêu rõ các lĩnh vực được (không) đăng trên ObjectOutputStream mà không sử dụng tạm thời hoặc serialPersistentFields


Thông tin cơ bản: Tôi cần sử dụng chú thích để xác định thành viên của lớp nào sẽ được đăng theo thứ tự (hoặc tốt hơn: không được đăng theo thứ tự). Các lớp liên quan phải triển khai giao diện Serializable, nhưng không phải Externalizable, vì vậy tôi không muốn triển khai thuật toán tuần tự hóa/deserialization cho từng đối tượng mà chỉ sử dụng chú thích cho nó. Tôi không thể sử dụng từ khóa transient, vì chú thích yêu cầu một số kiểm tra thêm để xác định xem một trường có nên được tuần tự hóa hay không. Những kiểm tra này phải được thực hiện bởi ObjectOutputStream (hoặc trong lớp con của riêng tôi là ObjectOutputStream). Tôi cũng không thể xác định một serialPersistentFields -array trong mỗi lớp, bởi vì như đã giải thích trước đây, tại thời gian biên dịch nó không được xác định các trường nào sẽ được tuần tự hóa.

Vì vậy, điều duy nhất cần được chú ý trong lớp bị ảnh hưởng là chú thích ở cấp trường (@Target(ElementType.FIELD)).

Tôi đã thử khá nhiều cách tiếp cận trong vài ngày qua, nhưng đã không tìm thấy một trong đó đang làm việc:


Các ObjectOutputStream có một phương pháp writeObjectOverride(Object) mà có thể được sử dụng để xác định một riêng thực hiện quá trình tuần tự hóa khi mở rộng ObjectOutputStream. Điều này chỉ hoạt động nếu ObjectOutputStream được khởi tạo với không có đối số-constructor vì nếu không writeObjectOverride sẽ không bao giờ được gọi. Nhưng cách tiếp cận này đòi hỏi tôi phải thực hiện toàn bộ quá trình tuần tự hóa một mình và tôi không muốn làm điều này, vì nó khá phức tạp và đã được thực hiện bởi mặc định ObjectOutputStream. Tôi đang tìm một cách để chỉ sửa đổi việc thực hiện serialization mặc định.


cách tiếp cận khác đã được mở rộng ObjectOutputStream một lần nữa và trọng writeObjectOverride(Object) (sau khi gọi enableReplaceObject(true)). Trong phương pháp này, tôi đã thử sử dụng một số loại SerializationProxy (xem What is the Serialization Proxy Pattern?) để đóng gói đối tượng được tuần tự hóa trong một proxy xác định danh sách các trường cần được tuần tự hóa. Nhưng cách tiếp cận này cũng không thành công như writeObjectOverride sau đó cũng được gọi cho Danh sách các trường (List<SerializedField> fields) trong Proxy dẫn đến một vòng lặp vô hạn.

Ví dụ:

public class AnnotationAwareObjectOutputStream extends ObjectOutputStream {  
    public AnnotationAwareObjectOutputStream(OutputStream out) 
      throws IOException { 
     super(out); 
     enableReplaceObject(true); 
    } 

    @Override 
    protected Object replaceObject(Object obj) throws IOException { 
     try { 
      return new SerializableProxy(obj); 
     } catch (Exception e) { 
      return new IOException(e); 
     } 
    } 

    private class SerializableProxy implements Serializable { 
     private Class<?> clazz; 
     private List<SerializedField> fields = new LinkedList<SerializedField>(); 

     private SerializableProxy(Object obj) throws IllegalArgumentException, 
       IllegalAccessException { 
      clazz = obj.getClass(); 
      for (Field field : getInheritedFields(obj.getClass())) { 
       // add all fields which don't have an DontSerialize-Annotation 
       if (!field.isAnnotationPresent(DontSerialize.class)) 
        fields.add(new SerializedField(field.getType(), field 
          .get(obj))); 
      } 
     } 

     public Object readResolve() { 
      // TODO: reconstruct object of type clazz and set fields using 
      // reflection 
      return null; 
     } 
    } 

    private class SerializedField { 
     private Class<?> type; 
     private Object value; 

     public SerializedField(Class<?> type, Object value) { 
      this.type = type; 
      this.value = value; 
     } 
    } 

    /** return all fields including superclass-fields */ 
    public static List<Field> getInheritedFields(Class<?> type) { 
     List<Field> fields = new ArrayList<Field>(); 
     for (Class<?> c = type; c != null; c = c.getSuperclass()) { 
      fields.addAll(Arrays.asList(c.getDeclaredFields())); 
     } 
     return fields; 
    } 

} 

// I just use the annotation DontSerialize in this example for simlicity. 
// Later on I want to parametrize the annotation and do some further checks 
@Target(ElementType.FIELD) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface DontSerialize { 
} 

Khi tôi phát hiện ra rằng người ta có thể sửa đổi bổ trong thời gian chạy (xem Change private static final field using Java reflection) Tôi cố gắng để thiết lập thoáng qua-Modifier trong thời gian chạy nếu chú thích tương ứng đã được thiết lập. Thật không may điều này cũng không hoạt động, bởi vì cách tiếp cận được sử dụng trong liên kết trước đó dường như chỉ hoạt động trên các trường tĩnh. Khi cố gắng nó với các lĩnh vực không tĩnh nó chạy mà không có một ngoại lệ nhưng không được tồn vì là trông giống như Field.class.getDeclaredField(...) nhuận trường hợp mới của các lĩnh vực bị ảnh hưởng mỗi khi nó được gọi là:

public void setTransientTest() throws SecurityException, 
      NoSuchFieldException, IllegalArgumentException, 
      IllegalAccessException { 
     Class<MyClass> clazz = MyClass.class; 
     // anyField is defined as "private String anyField" 
     Field field = clazz.getDeclaredField("anyField"); 

     System.out.println("1. is " 
       + (Modifier.isTransient(field.getModifiers()) ? "" : "NOT ") 
       + "transient"); 

     Field modifiersField = Field.class.getDeclaredField("modifiers"); 
     boolean wasAccessible = modifiersField.isAccessible(); 
     modifiersField.setAccessible(true); 
     modifiersField.setInt(field, field.getModifiers() | Modifier.TRANSIENT); 
     modifiersField.setAccessible(wasAccessible); 

     System.out.println("2. is " 
       + (Modifier.isTransient(field.getModifiers()) ? "" : "NOT ") 
       + "transient"); 

     Field field2 = clazz.getDeclaredField("anyField"); 

     System.out.println("3. is " 
       + (Modifier.isTransient(field2.getModifiers()) ? "" : "NOT ") 
       + "transient");  
} 

Đầu ra là:

1. is NOT transient 
2. is transient 
3. is NOT transient 

Vì vậy, sau khi gọi lại getDeclaredField (Field field2 = clazz.getDeclaredField("anyField");) nó đã mất công cụ sửa đổi tạm thời.


phương pháp tiếp theo:
Mở rộng ObjectOutputStream và ghi đè ObjectOutputStream.PutField putFields() và xác định một riêng PutField-thực hiện. PutField cho phép bạn chỉ định các trường (bổ sung) nào được tuần tự hóa nhưng tiếc là giao diện chỉ có nhiều phương thức của biểu mẫu put(String name, <type> val) và khi triển khai thực hiện chúng tôi không thể kết hợp các lời gọi phương thức với trường lớp mà nó được gọi ra. Ví dụ khi tuần tự hóa một trường được khai báo là private String test = "foo", phương thức put("test", "foo") được gọi, nhưng tôi không thể kết hợp giá trị name (là test) với lớp chứa trường test vì không có tham chiếu đến lớp chứa sẵn và do đó không thể đọc chú thích được ghi chú cho trường test.


Tôi cũng thử một vài phương pháp khác nhưng như đã đề cập tôi đã không thể serialize thành công trên mọi lĩnh vực, ngoại trừ những người thân với chú thích DontSerialize hiện.

Một điều tôi cũng gặp phải là các thao tác ByteCode. Có lẽ nó là có thể với những nhưng tôi có một yêu cầu không sử dụng bất kỳ công cụ bên ngoài - nó cần phải được tinh khiết Java (1,5 hoặc 1,6).


Xin lỗi vì bài đăng này thực sự dài nhưng tôi chỉ muốn thể hiện những gì tôi đã thử và hy vọng rằng ai đó có thể giúp tôi. Cảm ơn trước.

+1

Đó là một số đọc nặng .... –

+0

Nó không rõ ràng với tôi tại sao bạn không thể sử dụng từ khóa tạm thời. Có vẻ như bạn cũng cần sử dụng Chú thích vì lý do nào đó, nhưng điều này không ngăn bạn sử dụng tạm thời. –

+1

Có thể sử dụng 'DontSerialize' là một ví dụ tồi. Cho phép nói chú thích của tôi được gọi là 'DontSerializeOnMondays' (không đặt câu hỏi mục đích của chú thích này, nó chỉ là một ví dụ). Khi ObjectOutputStream cần serialize một đối tượng, nó sẽ kiểm tra nếu ngày trong tuần là thứ hai và nếu như vậy, nó không tuần tự hóa trường mà chú thích được liên kết với nhưng tất cả các trường khác trong lớp. Vì vậy, khi viết mã của lớp có chứa trường, tôi không biết nếu quá trình tuần tự hóa sẽ được thực hiện vào thứ hai hay không - vì vậy tôi không biết liệu tôi có nên định nghĩa từ khóa 'transient' hay không. – m0m0

Trả lời

0

Tôi sẽ xem xét lại nếu "Tuần tự hóa" thực sự là điều bạn muốn làm. Cho rằng các quy tắc tuần tự hóa phụ thuộc vào một số logic được xác định trong thời gian chạy, quá trình Deserialization sẽ là một cơn ác mộng để viết. Tuy nhiên,

Vấn đề thú vị.

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