Java сериализация: readFields() извън readObject()?

ObjectInputStream.readFields() отговаря на условията само в рамките на private void readObject(ObjectInputStream) метод.

public ObjectInputStream.GetField readFields() throws IOException, ClassNotFoundException {
  SerialCallbackContext ctx = curContext;
  if (ctx == null) {
    throw new NotActiveException("not in call to readObject");
  }
...

Аз съм в ситуация, когато не мога да използвам сериализация по подразбиране за четене на обект (т.е. ObjectInputStream.defaultReadObject()) и не желая да внедрявам readObject() метод във всичките си класове. В идеалния случай бих искал да имам ownDefaultReadObject() метод, който ще конструира нов обект от сериализирани полета (напр. чрез отражение).

Някакви идеи?

Ако някой иска да знае повече. Имената на полетата в някои от моите класове бяха преименувани (напр. от обфускатор) на a, b, c и т.н. Такива класове бяха сериализирани с преименувани полета, използвайки сериализация на Java по подразбиране. Трябва да ги десериализирам до оригинални класове (знам двойки имена на полета за всеки клас; a=> fieldName, b=> age, c=>gender и т.н.).


person FoxyBOA    schedule 31.07.2015    source източник
comment
Желанията ви са едно, но какви са вашите причини да не прилагате readObject()?   -  person user207421    schedule 31.07.2015
comment
Не поставям под въпрос избора ви, но не би ли било по-лесно прилагането на readObject(), което делегира повикването към персонализиран манипулатор? По този начин, въпреки че трябва да внедрите метода във всеки клас, внедряването ще бъде само едно извикване на метод.   -  person biziclop    schedule 31.07.2015
comment
@EJP 16: Имам няколко стотици такива класове. Те могат да бъдат добавяни/премахвани в бъдеще. Не пожелавам на никого в dev. екип дори мисли за проблеми със сериализацията/десериализацията. Един от вариантите е, че трябва да използвам инструменти за байт код (напр. Javassist), за да добавя метод readObject() по време на процеса на изграждане, но бих искал да използвам по-просто решение.   -  person FoxyBOA    schedule 31.07.2015
comment
@biziclop: Да, обмислям това решение, но с инструменти за байт код. Дано съществува по-лесен.   -  person FoxyBOA    schedule 31.07.2015
comment
@FoxyBOA Бих казал, че вероятно няма по-лесно решение. Но това е само предположение въз основа на общия начин, по който сериализацията изглежда работи, може да има малко вероятна кука, към която можете да прикачите тази логика.   -  person biziclop    schedule 31.07.2015
comment
@biziclop: Друго (вероятно по-лесно) решение може да бъде изобщо да не се използва сериализация на Java в името на Kryo (code.google.com/p/kryo/wiki/V1Benchmarks), XStream (x-stream .github.io) или друга лека библиотека за сериализиране.   -  person FoxyBOA    schedule 31.07.2015
comment
@FoxyBOA Мислех, че предпоставката е, че вече имате сериализирани данни, които не можете да промените. (Такива класове бяха сериализирани с преименувани полета, използвайки Java сериализация по подразбиране.)   -  person biziclop    schedule 31.07.2015
comment
@biziclop: Не, мога да избера почти всеки двигател за сериализация. В момента използваме db4o. За съжаление проектът е мъртъв и аз търся алтернативно решение. Моето предположение беше, че естествената сериализация на Java може да направи това, което имаме за сега. Заседнал съм с преименувани полета. Db4o има дори специален API за такъв сценарий ...   -  person FoxyBOA    schedule 31.07.2015
comment
@FoxyBOA XStream също е доста гъвкав в това отношение.   -  person biziclop    schedule 31.07.2015
comment
Как попаднахте в ситуация, в която се нуждаете от персонализирана сериализация за стотици класове?   -  person user207421    schedule 31.07.2015
comment
@EJP: Дълга история. Използваме сериализация (само нативна друга) за транспортен модул между производствената среда и тази за разработка. В производството нашите класове се обработват (т.е. обфускирани) и в dev. среда, от която се нуждаем оригинална (напр. за тестове). Имаме сложна йерархия на обекти и само обфускаторът знае какво (и как) преименува следващия път.   -  person FoxyBOA    schedule 31.07.2015


Отговори (1)


За да преименувате полета от обектен поток, методът, който трябва да замените, е ObjectInputStream.readClassDescriptor, който връща ObjectStreamClass.

Инстанции ObjectStreamClass изпълняват една от двете различни роли чрез големи различни подмножества на интерфейса. За избягване на съмнения, този избор на дизайн не трябва да се копира.

  • Описва полетата на сериализиращ се клас, работещ в текущия екземпляр на JVM. Намерете тези екземпляри чрез ObjectStreamClass.lookup.
  • Описва полетата на сериализиращ се клас, както са представени в конкретен сериализиран поток. Тези екземпляри се връщат от реализациите на ObjectInputStream.readClassDescriptor.

Във вашето обаждане за отмяна super.readClassDescriptor. Това ще прочете данните от потока. Заменете стойността от потока с такава, която има новите имена на полета, ако това е клас, който ви интересува.

Как да създадете свой собствен ObjectStreamClass? Напишете фиктивен екземпляр на класовете, които ви интересуват, в ObjectOutputStream. Можете да направите това като част от построеното, като просто запазите двоичните данни. Прочетете с друг ObjectInputStream със заменен readClassDescriptor, за да скриете дескрипторите.

ObjectInputStream.defaultReadObject/readFields не биха имали смисъл извън readObject (или подобни), защото разчитат на текущия десериализиращ обект, а не на аргумент. Има други ограничения за предотвратяване на друг код, извикващ defaultReadObject за пренаписване на полета, които трябва да останат постоянни, копирани валидирани, проверени за сигурност или подобни.

person Tom Hawtin - tackline    schedule 15.07.2019
comment
Бих написал някакъв код, за да направя това, но в момента правя други неща. Вероятно малко късно за оригиналния плакат, а също и за Java Serialization. / (Първоначално публикувах този отговор на грешни въпроси, защото съм идиот.) - person Tom Hawtin - tackline; 15.07.2019
comment
можете да използвате отражение, за да присвоите променлива, разгледайте моето решение - person Archmede; 14.08.2019