是否有任何标准方式或任何实用程序库可以读取/导航序列化(通过ObjectOutputStream)对象的属性?
我试图解决的问题是升级使用ObjectOutputStream(旧版)序列化并存储在数据库中的数据。在我的例子中,一些内部字段被彻底改变并重命名。我无法使用ObjectInputStream读回对象,因为更改字段的值将丢失(设置为null)。
特别是将来可能需要再次升级它,所以如果我能用XML序列化替换以这种方式存储的旧数据会更好。但是,完成此任务的一般方法是迭代属性(它们的名称,类型和值)。我无法找到从序列化数据中读取此类元数据的标准方法(例如, jackson 库可以将JSON读取为对象或属性和地图的映射,您可以容易操纵)。
是否有任何低级库可以处理数据,使用ObjectOutputStream进行序列化?结果输出看起来像包含有关序列化字段名称及其类型的信息。作为最后的手段,我可以理清这种格式,但我认为有人可以做到这一点,但我自己找不到任何图书馆。
例如,我有一个班级
public class TestCase implements Serializable
{
int id;
double doubleValue;
String stringValue;
public TestCase(int id, double doubleValue, String stringValue)
{
this.id = id;
this.doubleValue = doubleValue;
this.stringValue = stringValue;
}
}
已更改为
public class TestCase implements Serializable
{
ComplexId id;
double doubleValue;
String stringValue;
public TestCase(ComplexId id, double doubleValue, String stringValue)
{
this.id = id;
this.doubleValue = doubleValue;
this.stringValue = stringValue;
}
}
class ComplexId implements Serializable
{
int staticId;
String uuid;
public ComplexId(int staticId, String uuid)
{
this.staticId = staticId;
this.uuid = uuid;
}
}
升级价值本身并不是一个问题,我只是不知道如何偷看它并放回一个新的(使用新类型)而无需自定义实施序列化/反序列化协议(&#39对我来说是最后的手段。)
答案 0 :(得分:2)
如果您的版本控制系统包含原始.java文件,请使用ObjectInputStream读取信息。
另一个选项是根据Object Serialization Stream Protocol和Useful information about serialization手动读取字节数据。
我已经编写了这个样本来证明没有类文件的反序列化是可能的。不支持继承。它仅适用于原始类型字段和java.lang.String。
class CustomDeserialization {
public static class A implements Serializable {
private static final long serialVersionUID = 123124345135L;
int foo = 1;
String bar = "baz";
}
private byte[] bytes;
private int cursor;
CustomDeserialization(byte[] bytes) {
this.bytes = bytes;
}
private List<List<Object>> parse() {
cursor = 2; //skip STREAM_MAGIC
short classNameLength = getShort();
String className = getString(classNameLength);
cursor += 9; //skip serialVersionUID and flag tells object supports serialization
short numberOfFields = getShort();
List<List<Object>> result = new ArrayList<>();
List<Character> types = new ArrayList<>();
List<Object> values = new ArrayList<>();
List<String> classNames = new ArrayList<>();
for (int fieldIndex = 0; fieldIndex < numberOfFields; fieldIndex++) {
char c = getCharType();
types.add(c);
short fieldNameLength = getShort();
String fieldName = getString(fieldNameLength);
List<Object> objects = new ArrayList<>();
if (c == 'L') {
byte objectType = getByte();
if (objectType == ObjectStreamConstants.TC_REFERENCE) {
getShort();
objects.add(classNames.get(getShort() - 1));
} else {
short fieldClassNameLength = getShort();
String fieldClassName = getString(fieldClassNameLength);
classNames.add(fieldClassName);
objects.add(fieldClassName);
}
} else {
Class clazz = getCorrectType(c);
objects.add(clazz);
}
objects.add(fieldName);
result.add(objects);
}
cursor += 2; //skip TC_ENDBLOCKDATA & TC_NULL
for (int fieldIndex = 0; fieldIndex < numberOfFields; fieldIndex++) {
result.get(fieldIndex).add(getValue(types.get(fieldIndex), values));
}
return result;
}
private String getString(int lengthOfClassName) {
String s = new String(Arrays.copyOfRange(bytes, cursor, cursor + lengthOfClassName));
cursor += lengthOfClassName;
return s;
}
private char getCharType() {
char c = (char) (bytes[cursor] & 0xFF);
cursor++;
return c;
}
private char getChar() {
ByteBuffer bb = ByteBuffer.allocate(2);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.put(bytes[cursor + 1]);
bb.put(bytes[cursor]);
cursor += 2;
return bb.getChar(0);
}
private short getShort() {
ByteBuffer bb = ByteBuffer.allocate(2);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.put(bytes[cursor + 1]);
bb.put(bytes[cursor]);
cursor += 2;
return bb.getShort(0);
}
private double getDouble() {
ByteBuffer bb = ByteBuffer.allocate(8);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.put(bytes[cursor + 7]);
bb.put(bytes[cursor + 6]);
bb.put(bytes[cursor + 5]);
bb.put(bytes[cursor + 4]);
bb.put(bytes[cursor + 3]);
bb.put(bytes[cursor + 2]);
bb.put(bytes[cursor + 1]);
bb.put(bytes[cursor]);
cursor += 8;
return bb.getDouble(0);
}
private long getLong() {
ByteBuffer bb = ByteBuffer.allocate(8);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.put(bytes[cursor + 7]);
bb.put(bytes[cursor + 6]);
bb.put(bytes[cursor + 5]);
bb.put(bytes[cursor + 4]);
bb.put(bytes[cursor + 3]);
bb.put(bytes[cursor + 2]);
bb.put(bytes[cursor + 1]);
bb.put(bytes[cursor]);
cursor += 8;
return bb.getLong(0);
}
private byte getByte() {
byte b = bytes[cursor];
cursor++;
return b;
}
private int getInt() {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.put(bytes[cursor + 3]);
bb.put(bytes[cursor + 2]);
bb.put(bytes[cursor + 1]);
bb.put(bytes[cursor]);
cursor += 4;
return bb.getInt(0);
}
private float getFloat() {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.put(bytes[cursor + 3]);
bb.put(bytes[cursor + 2]);
bb.put(bytes[cursor + 1]);
bb.put(bytes[cursor]);
cursor += 4;
return bb.getFloat(0);
}
private boolean getBoolean() {
boolean b = bytes[cursor] == 1;
cursor++;
return b;
}
private Class getCorrectType(char type) {
switch (type) {
case 'B':
return byte.class;
case 'C':
return char.class; // char
case 'D':
return double.class; // double
case 'F':
return float.class; // float
case 'I':
return int.class; // integer
case 'J':
return long.class; // long
case 'S':
return short.class; // short
case 'Z':
return boolean.class; // boolean
case 'L':
return Object.class;
}
throw new IllegalArgumentException();
}
private Object getValue(char type, List<Object> values) {
switch (type) {
case 'B':
byte b = getByte();
values.add(b);
return b;
case 'C':
char c = getChar();
values.add(c);
return c; // char
case 'D':
double d = getDouble();
values.add(d);
return d; // double
case 'F':
float f = getFloat();
values.add(f);
return f; // float
case 'I':
int i = getInt();
values.add(i);
return i; // integer
case 'J':
long l = getLong();
values.add(l);
return l; // long
case 'S':
short s = getShort();
values.add(s);
return s; // short
case 'Z':
boolean b1 = getBoolean();
values.add(b1);
return b1; // boolean
case 'L':
byte objectType = getByte();
if (objectType == ObjectStreamConstants.TC_REFERENCE) {
getShort(); // skip 2 bytes
return values.get(getShort());
} else {
short stringValueLength = getShort();
String string = getString(stringValueLength);
values.add(string);
return string;
}
}
throw new IllegalArgumentException();
}
public static void main(String[] args) {
A a = new A();
try {
File file = new File("temp.out");
try (FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);) {
oos.writeObject(a);
oos.flush();
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
return;
}
try {
try (FileInputStream fis = new FileInputStream("temp.out");
ObjectInputStream ois = new ObjectInputStream(fis);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();) {
int cursor;
byte[] data = new byte[8192];
while ((cursor = fis.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, cursor);
}
byte[] bytes = buffer.toByteArray();
List<List<Object>> result = new CustomDeserialization(bytes).parse();
result.forEach(list -> {
list.forEach(o -> System.out.print(o + " "));
System.out.println();
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
答案 1 :(得分:-1)
我编译了所需类的旧版本,并更改了ClassLoader以在升级中加载它们,使用ObjectStream读取对象并使用XML对其进行序列化。然后我添加了XML结构的修复程序。
如果需要,我可以使用ClassLoader hack添加代码,但是AFAIR就是Stack Overflow上的某个地方。