1
votes

Extraire des objets imbriqués Json sous un chemin spécifique à l'aide du flux d'entrée en Java

Je cherche ceci depuis un moment, et j'ai aussi développé quelque chose qui fonctionne mais je me demande s'il existe un framework qui peut me faciliter la vie.

Le problème est simple, j'ai un Json InputStream représentant un très grand données utiles.

Je sais avec certitude que cette charge utile contient un tableau d'objets sous un chemin connu, et je ne veux pas analyser le flux en mémoire, je voudrais plutôt rechercher code> sur le fichier sur le chemin donné et extrayez tous les objets du tableau imbriqué un par un sous forme de Map.

Exemple: p >

{
   "store": {
      "book" : [

         {
           "isbn": "123",
           "author": "author",
           "title": "title",
         },
         ..... many more objects
      ]
   }
}

Ce dont j'ai besoin est de rechercher sur $ .store.book et d'extraire des objets imbriqués individuels pour un traitement ultérieur. p >

J'ai essayé JsonPath (jayway) mais la méthode d'analyse semble tout charger en mémoire.

J'ai ensuite utilisé Jackson avec la bibliothèque de flux mais la solution que j'ai est un peu alambiqué. Y a-t-il un moyen plus simple d'y parvenir?

Merci


4 commentaires

que veux-tu extraire exactement? et que voulez-vous faire avec ça?


Est-ce pertinent ce que je veux faire ensuite? Je veux juste obtenir une carte de String Object qui représente le ième élément de ce tableau sans tout charger en mémoire


C'est pertinent, vous allez le charger en mémoire si vous voulez le filtrer. Il peut s'agir d'une charge de chaque élément et de son rejet, mais c'est une charge de mémoire malgré tout. J'ai ajouté une implémentation de flux comme réponse ci-dessous.


Oui vous avez raison dans ce cas, je n'ai pas besoin de le filtrer mais de les convertir et de les écrire


3 Réponses :


0
votes

Une option serait de parcourir séquentiellement les données à un débit limité tel que 512 ou 1024 octets à la fois. Vous pouvez ensuite analyser les octets au format codé qui est probablement ISO-8859-1 ou UTF-8. Vous pouvez alors lire tous les octets jusqu'à ce qu'un délimiteur soit atteint, probablement ] pour signifier la fin du tableau (espérons-le). Vous pouvez ensuite utiliser Jackson ou Gson pour charger uniquement ce tableau d'objets.


1 commentaires

Eh bien, c'est encore plus compliqué que d'utiliser simplement l'API de streaming Jackson, je suppose



0
votes

Vous avez besoin de ParamTOFilterBy et FilterValue

Si vous insistez pour utiliser JSONPath la sélection ressemblerait à ceci:

JsonPath.read(jsonAsString, "$.store.book[?(@.ParamTOFilterBy==FilterValue)]")

Ceci question parle du filtrage d'une chaîne JSON. Opinion J'aime la mise en œuvre de Jackson dans cette réponse


4 commentaires

Malheureusement, cette solution lit tous les objets en mémoire


Ne pourriez-vous pas simplement le filtrer à la source? Créer un autre point de terminaison?


la source est un flux lui-même comme un fichier en s3


Regardez la troisième réponse à cette question.



0
votes

Si le tableau JSON qui vous intéresse peut être identifié par un pointeur JSON, alors un FilteringParserDelegate avec un JsonPointerBasedFilter peut faire le travail. Il s'agit essentiellement d'un analyseur de diffusion en continu qui saute jusqu'à ce que la cible soit trouvée. Ensuite, vous pouvez continuer à diffuser les jetons intéressants ou effectuer une liaison de données.

JsonPointer bookArray = JsonPointer.compile("/store/book");
processArrayElements(json, bookArray, System.out::println);

Pour imprimer les livres:

private static final ObjectMapper mapper = new ObjectMapper();
private static final JsonFactory factory = mapper.getFactory();

public static void processArrayElements(InputStream json, 
                                        JsonPointer pointerToArray,
                                        Consumer<Map<String, Object>> consumer)
    throws IOException {

  JsonParser parser = new FilteringParserDelegate(
      factory.createParser(json),
      new JsonPointerBasedFilter(pointerToArray), false, false);

  if (parser.nextToken() != JsonToken.START_ARRAY) {
    throw new IOException("Expected an array but found " + parser.currentToken());
  }

  while (parser.nextToken() != JsonToken.END_ARRAY) {
    consumer.accept(parser.readValueAs(Map.class));
  }
}


3 commentaires

Mise à jour pour permettre le traitement d'un seul livre à la fois au lieu de conserver l'ensemble du tableau en mémoire.


C'est exactement ce que je recherchais, ma version était essentiellement de réimplémenter cela, mais c'est une manière beaucoup plus concise de résoudre le problème. D'un autre côté, et un peu hors sujet, existe-t-il un moyen de forcer la désérialisation du mappeur d'objet à toujours forcer tous les nombres à une chaîne? J'ai essayé différentes options de configuration mais rien ne fonctionne


@ user2151096 Heureux de vous aider. Jackson contraindra les nombres à des chaînes lors de la soumission à un objet Java (par exemple, si vous avez désérialisé vers une classe Book au lieu d'une carte). Je ne connais pas un moyen facile de configurer Jackson pour désérialiser les nombres en tant que chaînes lors de la désérialisation vers une carte ou vers le modèle d'arbre.