ノーマライズ(Normalize)とカノニカライズ(Canonicalize)の違い

ノーマライズ(Normalize)とカノニカライズ(Canonicalize)の違いをまとめた。両方とも数学・コンピュータサイエンスの世界では正規化と翻訳され、構造の標準化・簡易化を意味するが、概念・処理内容は異なる。

※音楽に対するNormalizeは音の大きさを合わせること。これは毛色が異なる処理のため対象外とする

結論

Normalizeのゴールは項目内部での冗長削減である。

対して、Canonicalizeのゴールは項目間での横断的な比較体制の確立である。項目間に相違ないか確認できるよう表記を統一すると言っても良い。Normalizeにある冗長削減は必須でないが、Normalizeにない項目間比較のためのユニーク制約を含む文脈的な対応となる。

シンプルな例でイメージを掴む

数式の例:

  • Y = 32 + 2X + 8a + 4
  • 32 -2x + y = 8b+4

これら二つの数式をNormalize(冗長削減)すると、それぞれ『Y=36+2X+8a』、『28-2x+y=8b』に出来る。「同類項はまとめられるし、半角スペースは表現の無駄だから消して冗長を削減しよう」という話である。

Canonicalizeは目的によって実施事項が揺れる。

同じ表記にして、項目間に違いがあるか、同じ項目のことを表しているかを明らかにするのがCanonicalizeのゴールだから、式の目的により『同じ項目』の定義が変わってくる。

字ずらが違ったら別のもの扱いしたい:

両方の式がCanonicalize済みの状態。完全一致しないから、二つの式を比較出来て両者に相違ありと機械的に判断できる。

項目を維持した状態で比較したい:

『変数を昇順、定数を昇順(a, bは数値よりも先に記載)、末尾は = 0 で終える』と表記のルールを決めれば『-2X + Y -8a – 32 – 4 = 0』、『-2x + y – 8b + 32 – 4 = 0』にCanonicalize出来る。統一した表記方法になっているから二つの式を比較出来て大文字X, 小文字xの差により両者に相違ありと機械的に判断出来る。

簡素化した式に相違ないか比較したい:

大文字小文字に意味がなく、定数のa, b, c もただの表記ゆれというなら二つの式は『-2x + y -8a – 36 = 0』、『-2x + y – 8a + 28 = 0』にCanonicalize出来る。統一した表記方法になっているから二つの式を比較出来て-36と28の差により両者に相違ありと機械的に判断出来る。

URLの例:

  • /home/canonical/shortcut/test/url/
  • /hoge/../test/url/
  • /test/url/index.html
  • /test/url/
  • /test/url

※shortcut は /test へのシンボリックリンクとする、index.html はDirectoryIndex設定済みする。

Normalizeすると、二つ目は『/test/url/』にNormalize(冗長削減)出来る

対して、末尾スラッシュありのURLにCanonicalizeすると5項目ともすべて /test/url/ に置き換えられ、同一の基準でURLを比較できる。

※例では静的URLのみを取り扱ったが、#での見出し有の場合や?でのクエリありの場合、更にそのヴァリエーションとして ?hoge=a&fuga=b、?fuga=b&hoge=a、?hoge=a&hoge=aa&fuga=bなんかも取り扱ってみるとよりCanonicalURLの学びになる

※一つのサーバー上でのURLを取り扱ったが、複数サーバーの存在を考慮してURLをCanonicaizeするならドメイン名/ホスト名も含めて考えることとなる

XMLの例:

  • <record data1=”x” data2=”y” />
  • <record data2=”y” data1=”x1″></record>

Normalizeすると二つ目は<record data2=”y” data1=”x” />に出来る。

『要素(data1, data2)を昇順に並べる』とルール化してCanonicalizeすると二つ目は<record data1=”x1” data2=”y” />となり、表記を統一したので内容が同じか比較しやすくなった。data1の値に相違があるため異なる内容を表すと比較出来る状態となった。

歴史

数学世界ではCanonicalizeは1900年代(1900~1910年)には存在した話。コンピューターを利用するようになり、データの表記揺れ対応や冗長削減対応が増えたから大きく注目を浴びるようになったようだ。

Google geocoding APIを試し結果を得る

Google geocoding API でキーワードをもとに緯度経度・住所を取得できる。事前準備・手続きから試用して結果XMLを取得するまでを解説した。

無料枠で使ってみるまでに必要な手続き

  • Googleアカウントを作っておく
  • https://console.cloud.google.com/getting-started にアクセスし、『有効なAPIとサービス』メニューからGeocoding APIを探し有効化
  • https://console.cloud.google.com/billing で請求先アカウントを追加しておく
  • https://console.cloud.google.com/apis/credentials でAPIキーを作成しておく(XXXXとする)

アクセスを試す

以下URLにアクセスし、結果に示すような画面出力を得られるのを確認する

https://www.google.co.jp/maps/api/geocode/xml?address=skytree&sensor=true&key=XXXX

※addressの値は検索キーワード、keyの値は作成したAPIキーに適宜変更すること

結果

<GeocodeResponse>
	<status>OK</status>
	<result>
		<type>establishment</type>
		<type>point_of_interest</type>
		<type>tourist_attraction</type>
		<formatted_address>日本、〒131-0045 東京都墨田区押上1丁目1−2</formatted_address>
		<address_component>
			<long_name>2</long_name>
			<short_name>2</short_name>
			<type>premise</type>
		</address_component>
		<address_component>
			<long_name>1</long_name>
			<short_name>1</short_name>
			<type>political</type>
			<type>sublocality</type>
			<type>sublocality_level_4</type>
		</address_component>
		<address_component>
			<long_name>1丁目</long_name>
			<short_name>1丁目</short_name>
			<type>political</type>
			<type>sublocality</type>
			<type>sublocality_level_3</type>
		</address_component>
		<address_component>
			<long_name>押上</long_name>
			<short_name>押上</short_name>
			<type>political</type>
			<type>sublocality</type>
			<type>sublocality_level_2</type>
		</address_component>
		<address_component>
			<long_name>墨田区</long_name>
			<short_name>墨田区</short_name>
			<type>locality</type>
			<type>political</type>
		</address_component>
		<address_component>
			<long_name>東京都</long_name>
			<short_name>東京都</short_name>
			<type>administrative_area_level_1</type>
			<type>political</type>
		</address_component>
		<address_component>
			<long_name>日本</long_name>
			<short_name>JP</short_name>
			<type>country</type>
			<type>political</type>
		</address_component>
		<address_component>
			<long_name>131-0045</long_name>
			<short_name>131-0045</short_name>
			<type>postal_code</type>
		</address_component>
		<geometry>
			<location>
				<lat>35.7100627</lat>
				<lng>139.8107004</lng>
			</location>
			<location_type>ROOFTOP</location_type>
			<viewport>
				<southwest>
					<lat>35.7089225</lat>
					<lng>139.8084778</lng>
				</southwest>
				<northeast>
					<lat>35.7116204</lat>
					<lng>139.8132971</lng>
				</northeast>
			</viewport>
		</geometry>
		<partial_match>true</partial_match>
		<place_id>ChIJ35ov0dCOGGARKvdDH7NPHX0</place_id>
		<plus_code>
			<global_code>8Q7XPR66+27</global_code>
			<compound_code>PR66+27 東京都墨田区</compound_code>
		</plus_code>
	</result>
</GeocodeResponse>

所感

知らない人が試すには手続きで時間がかかる。けれど、ブラウザのGETメソッドで簡単にデータを取得できるから物凄く手軽。

無料枠でもいろいろ試せる&楽しめたから、いろんな人に試してほしい機能だ。

VB .NET で複雑にネストされたJSONデータを取り扱う

Visual Basic .NETでJSONデータを取り扱うのにJson .NETが便利。JSON文字列データをDeserialize時の格納クラスを円滑に定義するための備忘録である。

※Json .NET:NuGet上パッケージ名は NewtonSoft.Json

課題

Json .NET を用いると、以下コードのようにSourceのJSON文字列をMyClassの形式に格納出来る。

Dim StructuredData = JsonConvert.DeserializeObject(Of MyClass)(source)

しかしこのMyClassをどう定義するかが初心者には難しい。

もっと具体的に言えば、YouTube Data API V3のデータを取り扱おうと思っても、入れ子にされたデータに対してどうクラスを定義すればStructuredDataに格納できるかに悩む。複雑なデータをお手軽快適に取り扱うのが課題だった。

結論

直下のデータ

基本的なデータの取り扱い

配列ではない個別項目にアクセスしたい場合はそれぞれプロパティを定義する。つまり、以下例であればPublic Property kind As String と定義し Debug.Print (StructuredData.kind) という形で使える。

{
__”kind”: “youtube#playlistItemListResponse”,
__”etag”: “XXX_XXXXXXXXXXXXX_X-XXXXXXX”,
__”nextPageToken”: “XXXXXXXXXXXXXX”,

Public Class MyClass
__Public Property kind As String
__Public Property etag As String
__Public Property nextPageToken As String
End Class

同じ階層の全データが同じデータ型のKey-Valueの形になっている場合

以下 pageInfo 項目内、totalResults, resultsPerPage は両方とも String – Long 構成とみなせる。

{
__”kind”: “youtube#playlistItemListResponse”,
__”etag”: “XXX_XXXXXXXXXXXXX_X-XXXXXXX”,
__”pageInfo”: {
____”totalResults”: 11,
____”resultsPerPage”: 10
__}
}

この場合はPublic Property kv As Dictionary(Of DataTypeKey, DataTypeValue) の形で定義すればDebug.Print (kv(“kind”))のように必要項目のみKEY指定して扱える。

Public Class MyClass
__Public Property pageInfo As Dictionary(Of String, Long)
End Class

更に下の階層のデータを持つ項目がある場合・データ型が異なる場合

Public Class MyClass
__Public Property kind As String
__Public Property etag As String
__Public Property pageInfo As Dictionary(Of String, Long)
End Class

個別にプロパティを設けて厳密にアクセスしたい場合は個別に項目を定義する。つまり、Public Property kind As String と定義し Debug.Print (StructuredData.kind) という形で使える。

配列データ

変数名の右に [ 記号でくくってある場合は配列データである。

.NET で規定されている型のデータが配列になっている場合

以下構成であれば Public Property name As List(Of String) で定義すれば良い。 ※YouTubeAPI出力になかったので動作未確認。

__”name”: [
____”AAA”,
____”BBB”
__],

Public Class MyClass
__Public Property name As List(Of String)
End Class

.NET で規定されていない型のデータが配列になっている場合

対象の配列(下の例であればitems, snippet)それぞれをまるまるクラスとして定義し、上述の応用で配列のやり方(Public Property items As List(Of NestedClass), Public Property snippet As NestedNestedClass)で配列項目または個別項目と決めればよい。

{
__”kind”: “youtube#playlistItemListResponse”,
__”etag”: “XXX_XXXXXXXXXXXXX_X-XXXXXXX”,
__”nextPageToken”: “XXXXXXXXXXXXXX”,
__”items”: [
____{
______”kind”: “youtube#playlistItem”,
______”etag”: “XXXXXXXXXXXXXXXXXXXX_XXXXXX”,
______”id”: “XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX”,
______”snippet”: {
________”publishedAt”: “2022-02-20T17:23:00Z”,
________”resourceId”: {
__________”kind”: “youtube#video”,
__________”videoId”: “XXXXXXXXXXX”
________},

Public Class MyClass
__Public Property kind As String
__Public Property etag As String
__Public Property nextPageToken As String
__Public Property items As List(Of NestedClass)
End Class

Public Class NestedClass
__Public Property kind As String
__Public Property etag As String
__Public Property id As String
__Public Property snippet As NestedNestedClass
End Class

Public Class NestedNestedClass
__Public Property publishedAt As Date
__Public Property resourceId As Dictionary(Of String, String)
End Class

調査実施環境:

  • Windows 11
  • Visual Studio .NET 2019
  • .NET Core 3.1
  • Json .NET .NET Standard 2.0 Version 13.0.1.25517