🔗

URLを解析するQsを丁寧に説明してみた

7 min read

Qsって何?

npm公式

クエリの解析,文字列化をするライブラリです.
使っていて非常に便利なので一旦まとめようと思います.

基本的な使い方

import qs from 'qs';  // (以下省略)

const obj = qs.parse('a=c');  // { a: 'c' }

const str = qs.stringify(obj);  // 'a=c'

簡単に言うと,parseはURLなどの文字列をオブジェクトに変換するメソッドで, stringifyはオブジェクトをURL形式の文字列に変換するメソッドです.


またqsはネストしたオブジェクトも生成することができます.
例えば'foo[bar]=baz'

foo: {
  bar: 'baz'
}

を生成します.
このように[]を使ってサブキーを作成することができます.

Parseについて

オブションパラメータ

qsはいくつかオプションパラメータをとることができます.

  • plainObjects
const nullObject = qs.parse('a[hasOwnProperty]=b', { plainObjects: true });

このようにするとObject.create(null)で生成されたオブジェクトとして返されます.つまりプロトタイプメソッドが使えません.

  • allowPrototypes
const protoObject = qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true });

これはプロトタイプのプロパティを変更することができますが,使用時に問題が起きることがあり,推奨されていません.

WARNING It is generally a bad idea to enable this option as it can cause problems when attempting to use the properties that have been overwritten. Always be careful with this option.

  • ignoreQueryPrefix
    このオプションをつけることでクエスチョンマークから始まる文字列も解析することができます.
const nomal = qs.parse('?a=b&c=d');  // { ?a: "b", c: "d" }

const prefixed = qs.parse('?a=b&c=d', { ignoreQueryPrefix: true });  // {a: "b", c: "d"}
  • delimiter
    このオプションをつけることで区切りもじを考えずに解析することができます.
const delimited = qs.parse('a=b;c=d', { delimiter: ';' });  // { a: 'b', c: 'd' }
const regexed = qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ });  // { a: 'b', c: 'd', e: 'f' }

delimiterは正規表現も使用することができます.

  • allowDots
    このオプションをつけることでドットのオブジェクト表記を解析することができます.
const withDots = qs.parse('a.b=c', { allowDots: true });  // { a: { b: 'c' } }

こんなのもできる

  • URL encodeされた文字列の解析
    qsはURL encodedされた文字列もきちんとオブジェクトにすることができます.
const urlEncoded = qs.parse('a%5Bb%5D=c');  // a: { b: "c" }
  • ネストされた文字列の解析
    ネストしたような構造も正しく解析することができます.
const nested = qs.parse('foo[bar][baz]=foobarbaz');
/**
{
  foo: {
    bar: {
      baz: 'foobarbaz'
    }
  }
}
*/

しかしネストには限界があります. 5つの子の深さまで解析することができます.

例えば'a[b][c][d][e][f][g][h][i]=j'を解析しようとすると,
{ a: { b: { c: { d: { e: { f: { '[g][h][i]': 'j' } } } } } } };
このようになります.

  • ネストする深さを設定できる
    上記ではネストの深さは5つの子までと説明しましたが,オプションで変更できます.
const deep = qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 });

このdepthは適度に小さな値が好ましいと言われています.

The depth limit helps mitigate abuse when qs is used to parse user input, and it is recommended to keep it a reasonably small number.

  • 解析するパラメータの上限を設定できる
    qsはデフォルトでは1000つのパラメータしか解析しません.ですがparameterLimitというオプションを使って解析するパラメータ数を制限することができます.
const limited = qs.parse('a=b&c=d', { parameterLimit: 1 });  // { a: 'b' }

配列型の解析

qsは配列型の文字列も解析できます.
例を見た方が分かりやすいと思います.

const withArray = qs.parse('a[]=b&a[]=c');  // { a: ['b', 'c'] }

インデックスをつけたものも解析できます.

const withIndexes = qs.parse('a[1]=c&a[0]=b');  // { a: ['b', 'c'] }

記号を混ぜるとオブジェクトとしてまとめられます.

const mixedNotation = qs.parse('a[0]=b&a[b]=c');  // { a: { '0': 'b', b: 'c' } 

オブジェクトの配列も作成できます.

const arraysOfObjects = qs.parse('a[][b]=c');  // { a: [{ b: 'c' }] }

コンマで結合された配列も解析することができます.

const arraysOfObjects = qs.parse('a=b,c', { comma: true });  // { a: ['b', 'c'] } 

配列のインデックスとオブジェクトのキーの唯一の違いは、配列を作成するためには、括弧の間の値が数値でなければならないことです。

Note that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number to create an array.

  • 空文字列の扱い
    空文字列も値として認識されます.
const withEmptyString = qs.parse('a[]=&a[]=b');  // { a: ['', 'b'] }
  • allowSparse
    このオプションは配列のインデックスをそのまま反映するかを表します.
const noSparse = qs.parse('a[1]=b&a[15]=c');  // { a: ['b', 'c'] }
const sparseArray = qs.parse('a[1]=2&a[3]=5', { allowSparse: true });  // { a: [, '2', , '5'] }
  • arrayLimit
    このオプションは配列のインデックスの制限を設定するものです.デフォルトでは20に設定されており,それより大きなインデックスを指定した場合は,代わりにその数字をキーとするオブジェクトになります.
const withArrayLimit = qs.parse('a[1]=b', { arrayLimit: 0 });  // { a: { '1': 'b' } }
  • parseArrays
    このオプションは配列の解析を完全に無効にするためのものです.
const noParsingArrays = qs.parse('a[]=b', { parseArrays: false });  // { a: { '0': 'b' } }

Stringifyについて

stringifyはオブジェクトなどをURLエンコードする際に使用できます.
基本的にはparseで見てきたような雰囲気で考えて貰えば大丈夫です.

const ex = qs.stringify({ a: 'b' });  // 'a=b'
const ex2 = qs.stringify({ a: { b: 'c' } });  // 'a%5Bb%5D=c'

空文字やnullは省略されますが,=は残ります.

const ex = qs.stringify({ a: '' });  // 'a='

しかしundefinedは全て省略されます.

const ex = qs.stringify({ a: null, b: undefined });  // 'a='

オプション

  • encode

encodeオプションをfalseにするとエンコードを行いません.

const unencoded = qs.stringify({ a: { b: 'c' } }, { encode: false });  // 'a[b]=c'
  • indices

配列を渡したときはデフォルトではインデックスが付与されますが,indicesfalseにして使うこともできます.

const ex = qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false });  // 'a=b&a=c&a=d'
  • addQueryPrefix
    このオプションを指定すると先頭にクエスチョンマークをつけて出力してくれます.
const ex = qs.stringify({ a: 'b', c: 'd' }, { addQueryPrefix: true });  // '?a=b&c=d' 
  • arrayFormat

arrayFormatオプションを指定することでアウトプットを変えることができます.

qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })
// 'a[0]=b&a[1]=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })
// 'a[]=b&a[]=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })
// 'a=b&a=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' })
// 'a=b,c'
  • delimiter
    このオプションをつけることで区切りを指定できます.
const ex = qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }
  • sort
    sortオプションによりパラメータをソートすることができます.
const ex = qs.stringify({ a: 'c', z: 'y', b : 'f' }, { sort: alphabeticalSort }  // 'a=c&b=f&z=y'

nullの扱いについて

先ほども説明しましたがstringifyするとき,空文字やnullの場合は値が省略されます.

const withNull = qs.stringify({ a: null, b: '' });
// 'a=&b='

parseの場合は=があってもなくても値がなければ空文字に変換されます.

const equalsInsensitive = qs.parse('a&b=');
// { a: '', b: '' }
  • strictNullHandling
    このオプションを使用するとnullの場合に=を省略できます.
const strictNull = qs.stringify({ a: null, b: '' }, { strictNullHandling: true });
// 'a&b='
  • skipNulls
    nullの値を全く表示させたくない時に使用できます.
const nullsSkipped = qs.stringify({ a: 'b', c: null}, { skipNulls: true });
// 'a=b'

終わりに

間違い等ありましたらコメントで教えていただけると嬉しいです.

Discussion

ログインするとコメントできます