web-dev-qa-db-ja.com

jqフィルターがJSONデータ構造からデータを正常にプルしたかどうかを確認するにはどうすればよいですか?

特定のフィルターがJSONデータ構造からデータをプルすることに成功したかどうかを知りたいです。例えば:

###### For the user steve...

% Name=steve
% jq -j --arg Name "$Name" '.[]|select(.user == $Name)|.value' <<<'
[
   {"user":"steve", "value":false},
   {"user":"tom", "value":true},
   {"user":"pat", "value":null},
   {"user":"jane", "value":""}
]'
false
% echo $?
0

注:成功した結果には、ブール値、null、さらには空の文字列が含まれる可能性があります。

###### Now for user not in the JSON data...

% Name=mary
% jq -j --arg Name "$Name" '.[]|select(.user == $Name)|.value' <<<'
[
   {"user":"steve", "value":false},
   {"user":"tom", "value":true},
   {"user":"pat", "value":null},
   {"user":"jane", "value":""}
]'
% echo $?
0

フィルタがJSONデータ構造からデータをプルしない場合は、これを知る必要があります。フィルタがゼロ以外のリターンコードを返すことをお勧めします。

セレクターがJSONデータ構造からデータを正常にプルするのか、データをプルできないのかを判断するにはどうすればよいですか?

重要:上記のフィルターは単なる例であり、ソリューションはすべてのjqフィルターで機能する必要があります。

注:評価環境はBash4.2以降です。

10
Steve Amerige

すべての要件を満たすソリューションを見つけました!ご意見をお聞かせください!

アイデアは、ファーストパスチェックとして_jq -e "$Filter"_を使用することです。次に、戻りコード1の場合、jq "path($Filter)"チェックを実行します。後者は、実際にJSONデータへのパスがある場合にのみ成功します。

Select.sh

_#!/bin/bash

Select()
{
   local Name="$1"
   local Filter="$2"
   local Input="$3"
   local Result Status

   Result="$(jq -e --arg Name "$Name" "$Filter" <<<"$Input")"
   Status=$?

   case $Status in
   1) jq --arg Name "$Name" "path($Filter)" <<<"$Input" >/dev/null 2>&1
      Status=$?
      ;;
   *) ;;
   esac

   [[ $Status -eq 0 ]] || Result="***ERROR***"
   echo "$Status $Result"
}

Filter='.[]|select(.user == $Name)|.value'
Input='[
   {"user":"steve", "value":false},
   {"user":"tom", "value":true},
   {"user":"pat", "value":null},
   {"user":"jane", "value":""}
]'

Select steve "$Filter" "$Input"
Select tom   "$Filter" "$Input"
Select pat   "$Filter" "$Input"
Select jane  "$Filter" "$Input"
Select mary  "$Filter" "$Input"
_

そして、上記の実行:

_% ./Select.sh
0 false
0 true
0 null
0 ""
4 ***ERROR***
_
1
Steve Amerige

-e / --exit-status からjq Manualフラグを使用できます。

最後の出力値がfalseでもnullでもなかった場合はjqの終了ステータスを0に設定し、最後の出力値がfalseまたはnullでなかった場合は1に設定し、有効な結果が生成されなかった場合は4に設定します。通常、jqは、使用上の問題またはシステムエラーがあった場合は2、jqプログラムのコンパイルエラーがあった場合は3、jqプログラムが実行された場合は0で終了します。

あなたの与えられた例は私のために働いていないので、私は以下のような基本的なフィルターでの使用法を示すことができます。

クエリを成功させるには、

dudeOnMac:~$ jq -e '.foo?' <<< '{"foo": 42, "bar": "less interesting data"}'
42
dudeOnMac:~$ echo $?
0

存在しないエンティティZooで行われた無効なクエリの場合、

dudeOnMac:~$ jq -e '.zoo?' <<< '{"foo": 42, "bar": "less interesting data"}'
null
dudeOnMac:~$ echo $?
1

エラーシナリオの場合、jq入力ストリームを二重引用符で囲んで作成したコード2を返します。

dudeOnMac:~$ jq -e '.zoo?' <<< "{"foo": 42, "bar": "less interesting data"}"
jq: error: Could not open file interesting: No such file or directory
jq: error: Could not open file data}: No such file or directory
dudeOnMac:~$ echo $?
2
11
Inian

Jqが現状であり、特にストリーム指向であることを考えると、より良いアプローチは、必要な区別を行う1つ以上のフィルターを定義して使用することだと思う傾向があります。したがって、フィールドの値にアクセスするために_.a_を記述するのではなく、get/1が次のように定義されていると仮定してget("a")を記述します。

_def get(f): if has(f) then .[f] else error("\(type) is not defined at \(f)") end;
_

これで、オブジェクトにキーがあるかどうかを簡単に判断でき、準備が整いました。このgetの定義は、配列でも使用できます。

1
peak

以下に更新されたソリューションを追加しました

ここでの基本的な問題は、_.key_または_.[key]_構文を使用してオブジェクトから値を取得しようとすると、jq — by definition —が欠落しているキーを区別できないことです。値がnullのキー。

代わりに、独自のルックアップ関数を定義できます。

_def lookup(k):if has(k) then .[k] else error("invalid key") end;
_

次に、次のように使用します。

_$ jq 'lookup("a")' <<<'{}' ; echo $?
jq: error (at <stdin>:1): invalid key
5

$ jq 'lookup("a")' <<<'{"a":null}' ; echo $?
null
0
_

次に、組み込みメソッドの代わりにlookupを一貫して使用すると、希望する動作が得られると思います。


bashを減らし、jqを増やす、別の方法があります。

_#!/bin/bash

lib='def value(f):((f|tojson)//error("no such value"))|fromjson;'

users=( steve tom pat jane mary )

Select () {
  local name=$1 filter=$2 input=$3
  local -i status=0
  result=$( jq --arg name "$name" "${lib}value(${filter})" <<<$input  2>/dev/null )
  status=$? 
  (( status )) && result="***ERROR***"
  printf '%s\t%d %s\n' "$name" $status "$result"
}

filter='.[]|select(.user == $name)|.value'

input='[{"user":"steve","value":false},
        {"user":"tom","value":true},
        {"user":"pat","value":null},
        {"user":"jane","value":""}]'

for name in "${users[@]}"
do
  Select "$name" "$filter" "$input"
done
_

これにより、次の出力が生成されます。

_steve   0 false
tom     0 true
pat     0 null
jane    0 ""
mary    5 ***ERROR***
_

これは、フィルターへの入力がないことはemptyのように機能し、空は_//_の代替をトリガーしますが、_"null"_や_"false"_のような文字列はトリガーしないという事実を利用します。

_value/1_は、オブジェクト/配列の単純なキー/インデックスルックアップであるフィルターでは機能しませんが、ソリューションでも機能しないことに注意してください。すべてのケースをカバーするには、このようなもの(またはあなたのもの)が必要になると確信していますandgetまたはlookupのようなもの。

0
Thedward