7
votes

Java: Affectation d'ID de référence d'objet pour la sérialisation personnalisée

Pour diverses raisons, j'ai une série de sérialisation personnalisée où je déblose des objets assez simples dans un fichier de données. Il y a peut-être 5-10 classes et les graphiques d'objet qui résultent sont acycliques et assez simples (chaque objet sérialisé contient 1 ou 2 références à une autre qui sont sérialisées). Par exemple:

class Foo
{
    final private long id;
    public Foo(long id, /* other stuff */) { ... }
}

class Bar
{
    final private long id;
    final private Foo foo;
    public Bar(long id, Foo foo, /* other stuff */) { ... }
}

class Baz
{
    final private long id;
    final private List<Bar> barList;
    public Baz(long id, List<Bar> barList, /* other stuff */) { ... }
}
  • Objets créés dynamiquement - ID est attribué à un compteur qui incrémente li>
  • Lecture d'objets à partir du disque - ID est attribué à partir du numéro stocké dans le fichier de disque li>
  • Singleton Objets - L'objet est créé avant tout objet créé de manière dynamique, de représenter un objet singleton toujours présent. Li> ul>

    Comment puis-je les gérer correctement? J'ai envie de réinventer la roue et il doit y avoir une technique bien établie pour la manipulation de tous les cas. P>


    Clarification: strong> Tout comme certaines informations tangentielles, le Format de fichier que je regarde est approximativement ce qui suit (brillant sur quelques détails qui ne devraient pas être pertinents). Il est optimisé de gérer une quantité assez importante de données binaires denses (dizaines / centaines de MB) avec la possibilité de intersperser des données structurées. Les données binaires denses représentent 99,9% de la taille du fichier. P>

    Le fichier consiste en une série de blocs corrigés par erreur qui servent de conteneurs. Chaque bloc peut être considéré comme contenant un réseau d'octets qui consiste en une série de paquets. Il est possible de lire les paquets un à la fois successivement (par exemple, il est possible de dire où se trouve la fin de chaque paquet, et le prochain démarre immédiatement après). P>

    Pour que le fichier puisse être considéré. En tant que série de paquets stockés sur une couche de correction d'erreur. La grande majorité de ces paquets sont des données binaires opaques qui n'ont rien à voir avec cette question. Une petite minorité de ces paquets, cependant, sont des éléments contenant des données structurées sérialisées, formant une sorte d'archipel "composé de données" îles "qui peuvent être liées par des relations de référence d'objet. P>

    donc je pourrais avoir un Fichier où Packet 2971 contient une FOO sérialisée, et le paquet 12083 contient une barre sérialisée qui fait référence à la FOO dans le paquet 2971. (avec paquets 0-2970 et 2972-12082 étant des paquets de données opaques) P>

    Tous ces paquets Tous sont immuables (et donnent donc les contraintes de la construction d'objets Java, ils forment un graphique d'objet acyclique), donc je n'ai pas à gérer les problèmes d'entretien. Ils sont également descendants d'une interface commune élément code>. Ce que je voudrais faire, c'est écrire un objet arbitraire objet code> dans le fichier. Si l'élément code> contient des références à un autre article code> S déjà dans le fichier, j'ai également besoin d'écrire celles du fichier, mais seulement s'ils n'ont pas encore été écrits . Sinon, j'aurai des doublons que j'aurai besoin de coalée d'une manière ou d'une autre lorsque je les lisons. P> p>


7 commentaires

Devez-vous vous soucier du cas où vous avez créé des objets, puis vous en chargerez certains du disque (avec IDS pouvant éventuellement entrer en conflit avec ceux déjà existants)?


Oui, et je pense que l'approche doit traiter les identifiants comme deux "espaces de noms" distincts et cartographier les identifiants préexistants en nouveaux.


Les objets doivent-ils aller dans des paquets séparés? Pourraient-ils tous être mis dans un grand paquet? Les paquets d'objet sont-ils mentionnés dans les données binaires opaques? Mon hunch est que les relations logiques se mêlent au stockage physique.


Les paquets d'objet sont-ils mentionnés à partir des données binaires opaques: indirectement oui (par exemple, ils sont interprétés à l'aide des paquets d'objet). Cela et le fait que je stilise ces paquets dans un fichier assez rapidement et que je souhaite les faire écrire le plus rapidement possible, rendez-vous assez important de les écrire dans un fichier peu après leur génération. Je pouvais jouer à des jeux avec les retenir en mémoire et traiter avec un processus de nettoyage qui gère le cas où le générateur de paquets est interrompu à mi-chemin par l'écriture de paquets sur le disque. Voici où il devient plus compliqué ....


(Et je traite cela comme un flux plutôt qu'un fichier d'accès aléatoire, à savoir je ne veux pas revenir en arrière et réécrire les portions du fichier plus tard - qui découplerait la commande de paquets de l'ordre dans lequel il est écrit dans un fichier mais à la Coût de la complexité)


Permettez-moi de vérifier: les données binaires sont liées aux objets. Donc, vous pourriez avoir un paquet d'objet, puis les paquets de données binaires suivants sont interprétés à l'aide de l'objet comme contexte?


Oui ... Eh bien, les paquets de données binaires sont interprétés en utilisant les objets qui sont apparus dans la mesure du contexte. (Les objets peuvent être considérés comme des "événements" qui sont de petits objets immuables représentant des modifications à un contexte mutable)


3 Réponses :


1
votes

Les FOOS sont-ils inscrits avec une foorégie? Vous pouvez essayer cette approche (assumer la barre et Baz disposer également de registres d'acquérir les références via l'ID).

Ceci a probablement beaucoup d'erreurs de syntaxe, d'erreurs d'utilisation, etc. Mais je sens que l'approche est une bonne. p>

Classe publique FOO { P>

void.writeToStream(OutputStream out) {
    out.print("<BAZ><id>" + id + "</id>");
    for(Bar bar : barList) out.println("<bar>" + bar.getId() + </bar>");
    out.print("</BAZ>");
}


0 commentaires

4
votes

Avez-vous vraiment besoin de faire cela? En interne, les pistes code> ObjectOutputStream code> quels objets ont déjà été sérialisés. Les écrivies ultérieures du même objet ne stockent que une référence interne (semblable à la rédaction de l'identifiant) plutôt que de rédiger à nouveau l'objet entier.

voir cache de sérialisation pour plus de détails. P>

Si les identifiants correspondent à une identité définie de l'extérieur, telle qu'une pièce d'identité, alors cela a du sens. Mais la question indique que les identifiants sont générés uniquement pour suivre quels objets sont sérialisés. P>

Vous pouvez gérer des singletons via la méthode code> code>. Une approche simple consiste à comparer l'instance fraîchement désérialisée avec vos instances singleton et s'il y a une correspondance, renvoyez l'instance Singleton plutôt que l'instance désérialisée. E.g. P>

public class LimitInputStream extends FilterInputStream
{
   private int bytesRead;
   private int limit;
   /** Reads up to limit bytes from in */
   public LimitInputStream(InputStream in, int limit) {
      super(in);
      this.limit = limit;
   }

   public int read(byte[] data, int offs, int len) throws IOException {
      if (len==0) return 0; // read() contract mandates this
      if (bytesRead==limit)
         return -1;
      int toRead = Math.min(limit-bytesRead, len);
      int actuallyRead = super.read(data, offs, toRead);
      if (actuallyRead==-1)
          throw new UnexpectedEOFException();
      bytesRead += actuallyRead;
      return actuallyRead;
   }

   // similarly for the other read() methods

   // don't propagate to underlying stream
   public void close() { }
}


10 commentaires

+1 Pour faire le point .... Est-ce que je dois vraiment faire ça? J'aimerais utiliser certaines installations construites dans le JRE, mais il y a tellement de différences entre ObjectOutputStream et ce que je fais que je ne sais pas comment relier les deux ensemble. Ma sérialisement est plus proche de la sérialisation XML.


Avez-vous essayé xstream - xstream.codehaus.org . C'est la sérialisation mais basée sur XML. Très pluggable. Il utilise également une série de cache de sérialisation - Les références à des objets déjà sérialisés sont écrites sous forme de références en XML, soit en référence à un ID généré automatiquement, soit à l'aide de XPath pour faire référence à l'élément d'origine défini l'objet. Vaut bien le look.


En fait, j'ai jumelé quelques minutes avant de poster un commentaire. Mon problème dans ce cas particulier, c'est que j'ai besoin de intersperser quelques objets complexes parmi un ensemble important d'octets de données brutes codés binaires qui doivent être stockés de manière optimisée car ils utilisent 99,9% de l'espace du fichier et je suis attendre des fichiers dans la gamme de 10-100 Mo. Donc, je ne peux pas utiliser xml ... tout ce que j'ai sont un tas d'îles déconnectées parmi un flux de données plus grand.


Xstream vous permet de remplacer complètement le format de fichier réel afin de pouvoir utiliser FastInfoSet ou une autre norme binaire. Je suppose que votre format de fichier vous permet de saisir les îlots de données et de traiter cela comme des "sous-projets" du flux principal. Ensuite, vous pourriez stocker ce que vous voulez là-bas, XML, FastInfoset, des tampons de protocole, etc. Juste parce que le reste de votre fichier est optimisé binaire, cela ne signifie pas que tout cela doit être. Vous pouvez utiliser des chunking pour diviser les îlots de données du reste du flux. Je vais élaborer plus dans ma réponse.


Question muette ... Comment implémentez-vous une sous-mère?


(par exemple, chaque bloc connaît sa propre longueur, je le fais))


Pas une question stupide - j'ai remis ma réponse.


Ok, je vois ce que vous obtenez. Peut-être que les "îles" étaient un mauvais terme; Vraiment ce que j'ai est un "archipel" de données. Je vais mettre à jour ma question à clarifier.


J'ai accepté ... J'ai fini par suivre les données comme "îles" et utiliser Google Gson pour encoder chacun une notation JSON. J'ai la possibilité de faire dupliquer certains des objets du fichier de données, mais c'est une petite partie de la taille du fichier que cela n'a pas d'importance pour la taille du fichier, et si je me soucie de l'équivalence des graphes d'objet, je peux coalecter plusieurs Copies d'objets équivalents lors de la lecture du fichier.


Ça semble bon. J'allais proposer d'étendre ObjectOutputStream afin qu'il écrive aux paquets de données appartenant à un objet après la diffusion de l'objet. Cela préservera ensuite le graphique d'objet, sans doublons, tout en permettant à chaque objet d'écrire les données qui lui appartiennent.



1
votes

J'ai l'impression de réinventer la roue et il doit y avoir une technique bien établie pour la manipulation de tous les cas.

Oui, on dirait que la sérialisation de l'objet par défaut ferait ou, finalement, vous pré-optimiser.

Vous pouvez modifier le format des données sérialisées (comme le Xmlencoder fait) pour un autre plus pratique.

mais Si vous insistez, je pense que le singleton avec un compteur dynamique devrait faire, mais ne mettez pas le ID, dans l'interface publique du constructeur: xxx

de sorte que la classe pourrait être une "séquence" et probablement une longue serait plus appropriée pour éviter avoir des problèmes avec le INTEGER.MAX_VALUE .

à l'aide d'un Atomiclong comme décrit dans le java.util.concurrent.atomic emballage (pour éviter d'avoir deux threads attribuer le même identifiant ou pour éviter Une synchronisation excessive) aiderait aussi. xxx

maintenant dans chaque classe que vous avez xxx

et c'est tout.

(note, je ne m'en souviens pas si désérialisé L'objet invoque le constructeur, mais vous obtenez l'idée)


1 commentaires

??? Ceci est déroutant ... vous avez un séquenceur en classe avec des méthodes non statiques, mais vous appelez séquencer.next () comme si la méthode statique est suivante. En outre, j'apprécie l'aide mais je sais comment faire ce que vous dites d'instancier un comptoir; Ma question est plus dans le sens de la façon de gérer une affectation contre-basée sur ou lecture-retour du fichier ou un singleton statique. Je ne peux pas utiliser une seule approche pour les constructeurs