2017-10-10 2 views
3

(Il était difficile de trouver un titre qui résume le problème, alors n'hésitez pas à l'améliorer).Utilisation de jq pour analyser les clés présentes dans deux listes (même si elles n'existent peut-être pas dans l'une d'elles)

Je dispose d'un fichier JSON avec le contenu suivant:

{ 
    "Items": [ 
     { 
      "ID": { 
       "S": "ID_Complete" 
      }, 
      "oldProperties": { 
       "L": [ 
        { 
         "S": "[property_A : value_A_old]" 
        }, 
        { 
         "S": "[property_B : value_B_old]" 
        } 
       ] 
      }, 
      "newProperties": { 
       "L": [ 
        { 
         "S": "[property_A : value_A_new]" 
        }, 
        { 
         "S": "[property_B : value_B_new]" 
        } 
       ] 
      } 
     }, 
     { 
      "ID": { 
       "S": "ID_Incomplete" 
      }, 
      "oldProperties": { 
       "L": [ 
        { 
         "S": "[property_B : value_B_old]" 
        } 
       ] 
      }, 
      "newProperties": { 
       "L": [ 
        { 
         "S": "[property_A : value_A_new]" 
        }, 
        { 
         "S": "[property_B : value_B_new]" 
        } 
       ] 
      } 
     } 
    ] 
} 

Je voudrais manipuler les données à l'aide jq de telle sorte que pour chaque élément de Items[] qui a une nouvelle valeur pour property_A (sous newProperties liste) générer une sortie avec l'ID correspondant , vieux et nouveaux (voir ci-dessous la sortie désirée) des champs, quelle que soit la valeur que la propriété a dans les oldProperties liste. De plus, si property_A n'existe pas dans les oldProperties, je dois encore le vieux champ être peuplé avec une null (ou une chaîne fixe pour ce qu'il vaut la peine).

sortie souhaitée:

{ 
    "id": "id_Complete", 
    "old": "[property_A : value_A_old]", 
    "new": "[property_A : value_A_new]" 
} 
{ 
    "id": "ID_Incomplete", 
    "old": null, 
    "new": "[property_A : value_A_new]" 
} 

Note: Même si property_A n'existe pas dans les oldProperties liste, d'autres propriétés peuvent (et) existe.

Le problème que je suis face est que je ne suis pas en mesure d'obtenir une sortie lorsque la propriété désirée n'existe pas dans les oldProperties liste. Ma commande actuelle jq ressemble à ceci:

jq -r '.Items[] | 
    { id:.ID.S, 
     old:.oldProperties.L[].S | select(. | contains("property_A")), 
     new:.newProperties.L[].S | select(. | contains("property_A")) }' 

Ce qui ne fait que rendre la ID_Complete cas, alors que je dois l'autre aussi bien.

Y a-t-il un moyen d'y parvenir en utilisant cet outil?

Merci d'avance.

Répondre

1

Ce filtre produit la sortie souhaitée.

def parse: capture("(?<key>\\w+)\\s*:\\s*(?<value>\\w+)") ; 
def print: "[\(.key) : \(.value)]"; 
def norm: [.[][][] | parse | select(.key=="property_A") | print][0]; 

    .Items 
| map({id:.ID.S, old:.oldProperties|norm, new:.newProperties|norm})[] 

Run Sample (filtre suppose filter.jq et des données dans data.json)

$ jq -M -f filter.jq data.json 
{ 
    "id": "ID_Complete", 
    "old": "[property_A : value_A_old]", 
    "new": "[property_A : value_A_new]" 
} 
{ 
    "id": "ID_Incomplete", 
    "old": null, 
    "new": "[property_A : value_A_new]" 
} 

Try it online!

+0

Réponse impressionnante, merci. Tant ceci que l'autre travail. Avant d'en accepter une cependant, je vais les essayer dans le système qui est déjà en place et voir lequel des deux peut être mieux adapté à cela (puisque la question présentait un exemple de travail et réduit d'une petite partie des données). – Nacho

2

Votre liste de propriétés semblent être des valeurs d'un objet. Vous pouvez les mapper dans un objet pour ensuite différencier les objets, puis faire un rapport sur les résultats.

Vous pouvez faire quelque chose comme ceci:

def make_object_from_properties: 
     [.L[].S | capture("\\[(?<key>\\w+) : (?<value>\\w+)\\]")] 
    | from_entries 
    ; 
def diff_objects($old; $new): 
     def _prop($key): select(has($key))[$key]; 
     ([($old | keys[]), ($new | keys[])] | unique) as $keys 
    | [ $keys[] as $k 
     | ({ value: $old | _prop($k) } // { none: true }) as $o 
     | ({ value: $new | _prop($k) } // { none: true }) as $n 
     | (if $o.none     then "add" 
      elif $n.none     then "remove" 
      elif $o.value != $n.value then "change" 
             else "same" 
      end) as $s 
     | { key: $k, status: $s, old: $o.value, new: $n.value } 
     ] 
    ; 
def diff_properties: 
     (.oldProperties | make_object_from_properties) as $old 
    | (.newProperties | make_object_from_properties) as $new 
    | diff_objects($old; $new) as $diff 
    | foreach $diff[] as $d ({ id: .ID.S }; 
      select($d.status != "same") 
     | .old = ((select(any("remove", "change"; . == $d.status)) | "[\($d.key) : \($d.old)]") // null) 
     | .new = ((select(any("add", "change"; . == $d.status)) | "[\($d.key) : \($d.new)]") // null) 
    ) 
    ; 
[.Items[] | diff_properties] 

Cela donne le résultat suivant:

[ 
    { 
    "id": "ID_Complete", 
    "old": "[property_A : value_A_old]", 
    "new": "[property_A : value_A_new]" 
    }, 
    { 
    "id": "ID_Complete", 
    "old": "[property_B : value_B_old]", 
    "new": "[property_B : value_B_new]" 
    }, 
    { 
    "id": "ID_Incomplete", 
    "old": null, 
    "new": "[property_A : value_A_new]" 
    }, 
    { 
    "id": "ID_Incomplete", 
    "old": "[property_B : value_B_old]", 
    "new": "[property_B : value_B_new]" 
    } 
] 

Il semble que vos données sont dans une sorte de format codé aussi. Pour une solution plus robuste, vous devriez envisager de définir certaines fonctions pour les décoder. Envisager des approches trouvées here sur la façon dont vous pourriez le faire.

+0

Réponse impressionnante, merci. Tant ceci que l'autre travail. Avant d'en accepter une cependant, je vais les essayer dans le système qui est déjà en place et voir lequel des deux peut être mieux adapté à cela (puisque la question présentait un exemple de travail et réduit d'une petite partie des données). – Nacho

+0

Après plusieurs tests, j'ai pu mieux ajuster (avec moins de changements) la solution proposée dans l'autre réponse à mon système. C'est pourquoi je l'accepte, mais je tiens à vous remercier pour votre réponse détaillée, il était également très utile de mieux comprendre comment fonctionne jq. – Nacho