ObjectStream:有没有办法将序列化对象读取为属性映射?
ObjectStream: is there a way to read serialized object as properties map?
是否有任何标准方法或任何实用程序库 read/navigate 通过序列化(通过 ObjectOutputStream)对象的属性?
我要解决的问题是升级使用 ObjectOutputStream(遗留)序列化并存储在数据库中的数据。在我的例子中,一些内部字段被彻底改变并重命名。我无法使用 ObjectInputStream 读回对象,因为更改字段的值将丢失(设置为空)。
特别是以后可能需要再次升级,如果能把这样存储的旧数据换成XML序列化就更好了。但完成此任务的一般方法需要遍历属性(它们的名称、类型和值)。我无法找到从序列化数据中读取此类元数据的标准方法(例如,jackson 库可以将 JSON 作为对象或属性映射读取,地图,您可以轻松操作)。
是否有任何低级库可以处理数据,用 ObjectOutputStream 序列化?结果输出看起来包含有关序列化字段名称及其类型的信息。作为最后的手段,我可以整理格式,但我认为有人已经做到了,但我自己找不到任何库。
例如,我有一个 class
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;
}
}
升级价值本身不是问题,我只是不知道如何在没有 serialization/deserialization 协议的自定义实现(即对我来说是最后的选择。
如果您有原始 .java 文件的版本控制系统编译它们,使用 ObjectInputStream 读取信息。
另一种选择是根据Object Serialization Stream Protocol and Useful information about serialization手动读取字节数据。
我写了这个示例来证明没有 class 文件的反序列化是可能的。不支持继承。它仅适用于原始类型字段和 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();
}
}
}
我编译了需要的旧版本 类 并更改了 ClassLoader 以在升级时加载它们,使用 ObjectStream 读取对象并使用 XML 序列化它。然后我为 XML 结构添加了修复。
如果需要,我可以使用 ClassLoader hack 添加代码,但 AFAIR 它在 Stack Overflow 的某个地方。
是否有任何标准方法或任何实用程序库 read/navigate 通过序列化(通过 ObjectOutputStream)对象的属性?
我要解决的问题是升级使用 ObjectOutputStream(遗留)序列化并存储在数据库中的数据。在我的例子中,一些内部字段被彻底改变并重命名。我无法使用 ObjectInputStream 读回对象,因为更改字段的值将丢失(设置为空)。
特别是以后可能需要再次升级,如果能把这样存储的旧数据换成XML序列化就更好了。但完成此任务的一般方法需要遍历属性(它们的名称、类型和值)。我无法找到从序列化数据中读取此类元数据的标准方法(例如,jackson 库可以将 JSON 作为对象或属性映射读取,地图,您可以轻松操作)。
是否有任何低级库可以处理数据,用 ObjectOutputStream 序列化?结果输出看起来包含有关序列化字段名称及其类型的信息。作为最后的手段,我可以整理格式,但我认为有人已经做到了,但我自己找不到任何库。
例如,我有一个 class
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;
}
}
升级价值本身不是问题,我只是不知道如何在没有 serialization/deserialization 协议的自定义实现(即对我来说是最后的选择。
如果您有原始 .java 文件的版本控制系统编译它们,使用 ObjectInputStream 读取信息。
另一种选择是根据Object Serialization Stream Protocol and Useful information about serialization手动读取字节数据。
我写了这个示例来证明没有 class 文件的反序列化是可能的。不支持继承。它仅适用于原始类型字段和 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();
}
}
}
我编译了需要的旧版本 类 并更改了 ClassLoader 以在升级时加载它们,使用 ObjectStream 读取对象并使用 XML 序列化它。然后我为 XML 结构添加了修复。
如果需要,我可以使用 ClassLoader hack 添加代码,但 AFAIR 它在 Stack Overflow 的某个地方。