web-dev-qa-db-fra.com

Comment exclure un champ de la sérialisation de classe au moment de l'exécution?

Comment exclure un champ de classe du processus de sérialisation au moment de l'exécution? Il y a transitoire modificateur pour le temps de compilation, mais qu'en est-il de l'exécution? Je veux dire la sérialisation Java commune avec ObjectOutputStream, pas gson ou quelque chose.

Désolé je pense que je n'ai pas bien expliqué. Ce n'est pas exactement une question de sérialisation, mais de de - sérialisation. J'ai un lot de fichiers hérités et les manipule comme ceci:

public class Deserialize {

/**
 * @param args
 * @throws IOException 
 * @throws ClassNotFoundException 
 */
public static void main(String[] args) throws ClassNotFoundException, IOException {
    File file = new File("/home/developer/workspace/DDFS/some.ddf");
    HackedObjectInputStream in = new HackedObjectInputStream(new GZIPInputStream(new FileInputStream(file)));

    System.out.println("Attempt to open " + file.getAbsolutePath());
    Object obj = in.readObject();
    in.close();


}

 static class HackedObjectInputStream extends ObjectInputStream
    {

        /**
         * Migration table. Holds old to new classes representation.
         */
        private static final Map<String, Class<?>> MIGRATION_MAP = new HashMap<String, Class<?>>();

        static
        {
            MIGRATION_MAP.put("DBOBExit", Exit.class);
        }

        /**
         * Constructor.
         * @param stream input stream
         * @throws IOException if io error
         */
        public HackedObjectInputStream(final InputStream stream) throws IOException
        {
            super(stream);
        }

        @Override
        protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException
        {
            ObjectStreamClass resultClassDescriptor = super.readClassDescriptor();

            for (final String oldName : MIGRATION_MAP.keySet())
            {
                if (resultClassDescriptor.getName().equals(oldName))
                {
                    resultClassDescriptor = ObjectStreamClass.lookup(MIGRATION_MAP.get(oldName));   
                }
            }

            return resultClassDescriptor;
        }

    }

}

Ce code fonctionne bien pour la plupart des fichiers, mais certains fichiers sont jetés.

Exception in thread "main" Java.lang.ClassCastException: cannot assign instance of Java.awt.Polygon to field Exit.msgbackPt of type Java.awt.Point in instance of Exit
at Java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.Java:2053)

en raison de différentes versions de la classe Exit. Nouvelle version a de nouveaux champs. Erreur de disparition lorsque j'ajoute des éléments transitoires à de nouveaux champs, mais qu'un autre fichier commence à générer une exception (derniers fichiers).

Ainsi, puis-je ajouter des éléments transitoires à ces nouveaux fichiers au moment de l'exécution si je détecte un fichier séralisé hérité? Peut-être réflexion ou quelque chose?

23
gaponov

La documentation de ObjectOutputStream dit:

Le mécanisme de sérialisation par défaut d'un objet écrit la classe de l'objet, la signature de la classe et les valeurs de tous les champs non-transitoires et non statiques. Les références à d'autres objets (sauf dans les champs transitoires ou statiques) entraînent également l'écriture de ces objets.

Ainsi, lorsque vous déclarez une variable comme transitoire, elle doit être ignorée par ObjectOutputStream. Assurez-vous que vous utilisez le mot clé transient et non une annotation @Transient. Ces annotations sont utilisées par certains frameworks ORM pour marquer des champs qui ne sont pas supposés être enregistrés dans des bases de données. Ils n'ont pas de sens pour le framework de sérialisation buildin.

private transient String foo; // Field gets ignored by ObjectOutputStream
@Transient private String bar; // Treated normally by ObjectOutputStream (might mean something for some other framework)
41
Philipp

Vous pouvez utiliser le modificateur 'transitoire':

http://docs.Oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.3

2
Michael Schmiedgen

La représentation sérialisée d'une classe particulière appartient à la classe elle-même. Vous ne pouvez pas la modifier de manière externe. Vous pouvez définir la sous-classe la plus proche en définissant un comportement de sérialisation personnalisé, mais n'affectant que les objets de cette sous-classe, pas ceux du parent. type.

Si vous ne pouvez pas du tout modifier la classe en question, votre seule option consiste à sous-classer ObjectOutputStream et à remplacer replaceObject pour remplacer l'objet problématique lors de l'écriture par un autre contenant uniquement les données souhaitées, et le processus d'image miroir à la lecture. time (sous-classe ObjectInputStream et redéfinition de resolveObject).

1
Ian Roberts

Vous creusez le mauvais trou ici. Au lieu de communiquer avec les décisions d'exécution concernant les champs à sérialiser et de redéfinir readClassDescriptor (), vous devriez envisager de redéfinir readResolve ().

0
user207421