Замена имени класса в сериализованных данных - PullRequest
7 голосов
/ 12 января 2011

Я хочу заменить строку "com.oldpackage.className" на "com.newPackage.className" в потоке сериализованных данных. Эти сериализованные данные считываются из БД и обновляются после замены строки.

Я сталкиваюсь с некоторыми проблемами, когда делаю то же самое. Если вы уже догадались, это часть упражнения по рефакторингу. Существуют ли библиотеки, которые помогли бы мне манипулировать сериализованными данными? Если вы также можете прокомментировать какие-либо меры предосторожности или предостережения, это будет очень полезно.

Большое спасибо, Крис. П.С .: И старый класс, и новый класс не объявляют идентификатор serialversion как часть его полей.

Ответы [ 5 ]

5 голосов
/ 12 января 2011

Я не знаю, как сделать то, что вы пытаетесь, но я могу предложить вам «законное» решение. Реализуйте конвертер, который имеет как старые, так и новые пакеты в classpath и выполняет следующее: считывает данные из БД, десериализует их с использованием старого пакета, преобразует старые экземпляры в новые в java, а затем снова сохраняет новые данные в БД.

Это решение требует выполнения грязной работы, но это безопасно. Более того, результаты могут быть повторно использованы в будущем. Но я желаю вам удачи в поиске решения, которое просто заменяет имя класса в сериализованных данных.

4 голосов
/ 12 ноября 2016

Чтобы уточнить ответ Тома Хоутина, мне удалось реализовать его, используя следующий код:

public static ObjectInputStream getSwapOIS( InputStream in ,String fromClass,String toClass)
    throws IOException ,ClassNotFoundException {
    final String from="^"+fromClass,fromArray="^\\[L"+fromClass,toArray="[L"+toClass;
    return new ObjectInputStream(in) {
        protected Class<?> resolveClass(ObjectStreamClass desc)
            throws IOException, ClassNotFoundException
        {
            String name = desc.getName().replaceFirst(from, toClass);
            name = name.replaceFirst(fromArray, toArray);
            return Class.forName(name);
        }
        protected ObjectStreamClass readClassDescriptor()
            throws IOException, ClassNotFoundException
        {
            ObjectStreamClass cd = super.readClassDescriptor();
            String name = cd.getName().replaceFirst(from, toClass);
            name = name.replaceFirst(fromArray, toArray);
            if(!name.equals(cd.getName())) {
                cd = ObjectStreamClass.lookup(Class.forName(name));
            }
            return cd;
        }
    };
}

Обратите внимание, что вам также необходимо переопределить readClassDescriptor (). Он работает как для стандартных типов, так и для массивов, и вы даже можете изменить имя класса, а не только имя пакета. Просто сделай:

 InputStream in = new ByteArrayInputStream(classBytes);
 ObjectInputStream ois = getSwapOIS(    in,
                                        "com.oldpackage.className",
                                        "com.newpackage.newClassName");
 Object myObject= ois.readObject();
2 голосов
/ 12 января 2011

IIRC вы можете (с достаточными разрешениями) переопределить ObjectInputStream.resolveClassresolveProxyClass), чтобы вернуть Class с другими именами пакетов. Хотя, IIRC, вы не можете изменить простое имя класса.

1 голос
/ 12 января 2011

Могу ли я предложить альтернативный вариант? Формат сериализации Java имеет некоторые существенные проблемы при использовании для длительного хранения.

Поскольку вы проводите рефакторинг и вам, скорее всего, придется десериализовать все сохраненные данные тем или иным способом, вы можете рассмотреть Буферы протокола Google как альтернативный формат сериализации.

Это может занять немного больше работы, но оно разработано специально для длительного хранения и управления версиями.

Удачи

1 голос
/ 12 января 2011

Если данные, подлежащие рефакторингу, хранятся - в виде строки - в базе данных, одним из вариантов является использование SQL для простого обновления этих данных, например (в псевдо MySQL)

update mydata 
set serialized_data = 
replace(serialized_data, "com.oldpackage", "com.newpackage")

Вы, конечно, реорганизуете свой код Java перед попыткой перечитать эти данные из БД.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...