java – ObjectStream:有没有办法将序列化对象作为属性映射读取?

是否有任何标准方式或任何实用程序库来读取/导航序列化(通过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;
    }
}

升级值本身不是问题,我只是不知道如何查看并放回一个新的(使用新类型)而没有自定义的序列化/反序列化协议(这是我的最后手段).

最佳答案 如果您的版本控制系统包含原始.java文件,请使用ObjectInputStream读取信息.

另一种选择是根据Object Serialization Stream ProtocolUseful 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();
        }
    }
}
点赞