apeescape2.com
  • メイン
  • トレンド
  • 収益と成長
  • Webフロントエンド
  • データサイエンスとデータベース
データサイエンスとデータベース

JSONでの双方向関係のサポート

作成しようとしたことがあります JSON 双方向の関係(つまり循環参照)を持つエンティティを含むデータ構造?ある場合は、次の行に沿ってJavaScriptエラーが発生している可能性があります。 「UncaughtTypeError:循環構造をJSONに変換する」 。または、を使用するJava開発者の場合 ジャクソン ライブラリ、あなたは遭遇したかもしれません 「JSONを書き込めませんでした:ルートが原因で無限再帰(StackOverflowError)が発生しましたjava.lang.StackOverflowError」 。

SQLServerのクエリ最適化手法

JSON双方向関係チャレンジ

この記事では、これらのエラーが発生することなく、双方向の関係を含むJSON構造を作成するための堅牢な作業アプローチを提供します。



多くの場合、この問題に対して提示される解決策は、基本的に回避する回避策を伴いますが、実際には問題に対処しません。例には、次のようなJacksonアノテーションタイプの使用が含まれます @JsonManagedReference そして @JsonBackReference (シリアル化から後方参照を単に省略します)または @JsonIgnore 関係の片側を単に無視すること。あるいは、データ内のそのような双方向の関係や循環依存を無視するカスタムシリアル化コードを開発することもできます。

ただし、双方向の関係のどちらの側も無視したり省略したりしたくありません。エラーを発生させずに、両方向で保存したいと考えています。本物 解決 JSONで循環依存関係を許可し、開発者がそれらを修正するための追加のアクションを実行せずにそれらについて考えるのをやめることができるようにする必要があります。この記事は、そうするための実用的で簡単なテクニックを提供します。これは、の標準セットへの便利な追加として役立ちます。 ヒントと実践 今日のフロントエンド開発者向け。

単純な双方向の関係の例

この双方向の関係(循環依存)の問題が発生する一般的なケースは、子を持つ親オブジェクト(参照)があり、それらの子オブジェクトが親への参照を維持したい場合です。簡単な例を次に示します。

var obj = { 'name': 'I'm parent' } obj.children = [ { 'name': 'I'm first child', 'parent': obj }, { 'name': 'I'm second child', 'parent': obj } ]

上記を変換しようとするとparent JSONへのオブジェクト(たとえば、stringifyのようにvar parentJson = JSON.stringify(parent);メソッドを使用する)、例外 Uncaught TypeError:循環構造をJSONに変換しています スローされます。

上記の手法の1つ(@JsonIgnoreなどの注釈の使用など)を使用することも、上記の親への参照を子から削除することもできますが、これらは次の方法です。 回避する のではなく 解く 問題。本当に必要なのは、各双方向の関係を維持し、例外をスローせずにJSONに変換できる結果のJSON構造です。

ソリューションへの移行

解決に向けた潜在的に明白なステップの1つは、各オブジェクトに何らかの形式のオブジェクトIDを追加してから、子の親への参照を置き換えることです。 オブジェクト 親オブジェクトへの参照 id 。例えば:

事業計画の締結
var obj = { 'id': 100, 'name': 'I'm parent' } obj.children = [ { 'id': 101, 'name': 'I'm first child', 'parent': 100 }, { 'id': 102, 'name': 'I'm second child', 'parent': 100 } ]

このアプローチにより、双方向の関係または循環参照から生じる例外を確実に回避できます。しかし、まだ問題があり、これらの参照をシリアル化および逆シリアル化する方法を考えると、その問題が明らかになります。

問題は、上記の例を使用して、値「100」へのすべての参照が親オブジェクトを参照していることを知る必要があることです(これはそのidであるため)。これは、値「100」を持つ唯一のプロパティがparentである上記の例では問題なく機能します。プロパティ。しかし、値が「100」の別のプロパティを追加するとどうなるでしょうか。例えば:

obj.children = [ { 'id': 101, 'name': 'I'm first child', 'priority': 100, // This is NOT referencing object ID '100' 'parent': 100 // This IS referencing object ID '100' }, { 'id': 102, 'name': 'I'm second child', 'priority': 200, 'parent': 100 } ]

値「100」への参照がオブジェクトを参照していると仮定した場合、シリアル化/逆シリアル化コードがparentのときにそれを知る方法はありません。親オブジェクトのidを参照しているが、priorityの場合は値「100」を参照します。値「100」を参照します。これは、親オブジェクトのidを参照していません。 (そして、priorityも親オブジェクトのidを参照していると見なされるため、その値が親オブジェクトへの参照に誤って置き換えられます)。

この時点であなたは尋ねることができます、 「待ってください、あなたは明白な解決策を逃しています。プロパティ値を使用してオブジェクトIDを参照していると判断する代わりに、プロパティ名だけを使用してみませんか?」 確かに、それはオプションですが、非常に限定的なものです。これは、常に他のオブジェクト(「親」、「子」、「次」などの名前)を参照すると想定される「予約済み」プロパティ名のリストを事前に指定する必要があることを意味します。これは、 のみ これらのプロパティ名は、他のオブジェクトへの参照に使用でき、これらのプロパティ名が 常に 他のオブジェクトへの参照として扱われます。したがって、これはほとんどの状況で実行可能な代替手段ではありません。

したがって、プロパティ値をオブジェクト参照として認識することに固執する必要があるようです。しかし、これは、これらの値が必要になることを意味します 保証付き 他のすべてのプロパティ値から一意である必要があります。を使用して、一意の値の必要性に対処できます。 グローバル一意識別子(GUID) 。例えば:

var obj = { 'id': '28dddab1-4aa7-6e2b-b0b2-7ed9096aa9bc', 'name': 'I'm parent' } obj.children = [ { 'id': '6616c598-0a0a-8263-7a56-fb0c0e16225a', 'name': 'I'm first child', 'priority': 100, 'parent': '28dddab1-4aa7-6e2b-b0b2-7ed9096aa9bc' // matches unique parent id }, { 'id': '940e60e4-9497-7c0d-3467-297ff8bb9ef2', 'name': 'I'm second child', 'priority': 200, 'parent': '28dddab1-4aa7-6e2b-b0b2-7ed9096aa9bc' // matches unique parent id } ]

だからそれはうまくいくはずですよね?

はい。

だが…

メモリリークの原因となるもの

完全に自動化されたソリューション

私たちの最初の挑戦を忘れないでください。 JSONとの双方向の関係を持つオブジェクトを、例外を生成せずにシリアル化および逆シリアル化できるようにしたかったのです。上記のソリューションはこれを実現しますが、(a)各オブジェクトに何らかの形式の一意のIDフィールドを追加し、(b) 交換 対応する一意のIDを持つ各オブジェクト参照。これは機能しますが、この方法でオブジェクトを「手動で」変更しなくても、既存のオブジェクト参照を自動的に処理するソリューションをお勧めします。

理想的には、オブジェクトのセット(任意のプロパティとオブジェクト参照のセットを含む)をシリアライザーとデシリアライザーに(双方向の関係に基づいて例外を生成せずに)渡し、デシリアライザーによって生成されたオブジェクトを正確に一致させることができるようにする必要がありますシリアライザーに供給されたオブジェクト。

私たちのアプローチは、シリアライザーが自動的に一意のIDを作成して(GUIDを使用して)各オブジェクトに追加することです。次に、オブジェクト参照をそのオブジェクトのGUIDに置き換えます。 (シリアライザーはいくつかの固有のプロパティを使用する必要があることに注意してください 名前 これらのIDについても同様です。この例では、@idを使用しますおそらく、プロパティ名の前に「@」を付けるだけで、一意であることを確認できます。)次に、デシリアライザーは、オブジェクトIDに対応するGUIDをそのオブジェクトへの参照に置き換えます(デシリアライザーはシリアライザーも削除することに注意してください)。 -逆シリアル化されたオブジェクトからGUIDを生成し、それによってオブジェクトを正確に初期状態に戻します)。

したがって、例に戻って、次のオブジェクトのセットをフィードします。 そのまま 私たちのシリアライザーに:

var obj = { 'name': 'I'm parent' } obj.children = [ { 'name': 'I'm first child', 'parent': obj }, { 'name': 'I'm second child', 'parent': obj } ]

次に、シリアライザーが次のようなJSON構造を生成することを期待します。

{ '@id': '28dddab1-4aa7-6e2b-b0b2-7ed9096aa9bc', 'name': 'I'm parent', 'children': [ { '@id': '6616c598-0a0a-8263-7a56-fb0c0e16225a', 'name': 'I'm first child', 'parent': '28dddab1-4aa7-6e2b-b0b2-7ed9096aa9bc' }, { '@id': '940e60e4-9497-7c0d-3467-297ff8bb9ef2', 'name': 'I'm second child', 'parent': '28dddab1-4aa7-6e2b-b0b2-7ed9096aa9bc' }, ] }

(あなたは使用することができます JSONフォーマッター JSONオブジェクトを美しくするためのツール。)

次に、上記のJSONをデシリアライザーにフィードすると、元のオブジェクトのセットが生成されます(つまり、親オブジェクトとその2つの子が、相互に適切に参照します)。

何をしたいのか、どのようにしたいのかがわかったので、実装しましょう。

JavaScriptでのシリアライザーの実装

以下は動作サンプルです JavaScript 例外をスローせずに双方向の関係を適切に処理するシリアライザーの実装。

var convertToJson = function(obj) { // Generate a random value structured as a GUID var guid = function() { function s4() { return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); } return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); }; // Check if a value is an object var isObject = function(value) { return (typeof value === 'object'); } // Check if an object is an array var isArray = function(obj) { return (Object.prototype.toString.call(obj) === '[object Array]'); } var convertToJsonHelper = function(obj, key, objects) { // Initialize objects array and // put root object into if it exist if(!objects) { objects = []; if (isObject(obj) && (! isArray(obj))) { obj[key] = guid(); objects.push(obj); } } for (var i in obj) { // Skip methods if (!obj.hasOwnProperty(i)) { continue; } if (isObject(obj[i])) { var objIndex = objects.indexOf(obj[i]); if(objIndex === -1) { // Object has not been processed; generate key and continue // (but don't generate key for arrays!) if(! isArray(obj)) { obj[i][key] = guid(); objects.push(obj[i]); } // Process child properties // (note well: recursive call) convertToJsonHelper(obj[i], key, objects); } else { // Current object has already been processed; // replace it with existing reference obj[i] = objects[objIndex][key]; } } } return obj; } // As discussed above, the serializer needs to use some unique property name for // the IDs it generates. Here we use '@id' since presumably prepending the '@' to // the property name is adequate to ensure that it is unique. But any unique // property name can be used, as long as the same one is used by the serializer // and deserializer. // // Also note that we leave off the 3rd parameter in our call to // convertToJsonHelper since it will be initialized within that function if it // is not provided. return convertToJsonHelper(obj, '@id'); }

JavaScriptでのデシリアライザーの実装

以下は、例外をスローせずに双方向の関係を適切に処理するデシリアライザーのJavaScript実装のサンプルです。

var convertToObject = function(json) { // Check if an object is an array var isObject = function(value) { return (typeof value === 'object'); } // Iterate object properties and store all reference keys and references var getKeys = function(obj, key) { var keys = []; for (var i in obj) { // Skip methods if (!obj.hasOwnProperty(i)) { continue; } if (isObject(obj[i])) { keys = keys.concat(getKeys(obj[i], key)); } else if (i === key) { keys.push( { key: obj[key], obj: obj } ); } } return keys; }; var convertToObjectHelper = function(json, key, keys) { // Store all reference keys and references to object map if(!keys) { keys = getKeys(json, key); var convertedKeys = {}; for(var i = 0; i

これらの2つのメソッドを介してオブジェクトのセット(双方向の関係を持つオブジェクトを含む)を渡すことは、本質的に恒等関数です。つまり、convertToObject(convertToJson(obj)) === obj trueと評価されます。

モンテカルロシミュレーションファイナンスの例

Java / Jacksonの例

次に、このアプローチが一般的な外部ライブラリでどのようにサポートされているかを見てみましょう。たとえば、Javaでどのように処理されるかを見てみましょう。 ジャクソン 図書館。

@JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class, property='@id') public class Parent implements Serializable { private String name; private List children = new ArrayList(); public String getName() { return name; } public void setName(String name) { this.name = name; } public List getChildren() { return children; } public void setChildren(List children) { this.children = children; } } @JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class, property='@id') public class Child implements Serializable { private String name; private Parent parent; public String getName() { return name; } public void setName(String name) { this.name = name; } public Parent getParent() { return parent; } public void setParent(Parent parent) { this.parent = parent; } }

これらの2つのJavaクラス 親 そして 子 この記事の冒頭のJavaScriptの例と同じ構造を表します。ここでの主なポイントは、@JsonIdentityInfoを使用することです。これらのオブジェクトをシリアル化/逆シリアル化する方法をジャクソンに指示する注釈。

Windows10はどの言語で書かれていますか

例を見てみましょう:

Parent parent = new Parent(); parent.setName('I'm parent') Child child1 = new Child(); child1.setName('I'm first child'); Child child2 = new Child(); child2.setName('I'm second child'); parent.setChildren(Arrays.asList(child1, child2));

親インスタンスをJSONにシリアル化した結果、JavaScriptの例と同じJSON構造が返されます。

{ '@id': '28dddab1-4aa7-6e2b-b0b2-7ed9096aa9bc', 'name': 'I'm parent', 'children': [ { '@id': '6616c598-0a0a-8263-7a56-fb0c0e16225a', 'name': 'I'm first child', 'parent': '28dddab1-4aa7-6e2b-b0b2-7ed9096aa9bc' }, { '@id': '940e60e4-9497-7c0d-3467-297ff8bb9ef2', 'name': 'I'm second child', 'parent': '28dddab1-4aa7-6e2b-b0b2-7ed9096aa9bc' }, ] }

別の利点

JSONで双方向の関係を処理するための説明されたアプローチは、同じオブジェクトの冗長コピーを含める必要がなく、一意のIDだけでオブジェクトを参照できるため、JSONファイルのサイズを縮小するのにも役立ちます。

次の例を考えてみましょう。

{ '@id': '44f47be7-af77-9a5a-8606-a1e6df299ec9', 'id': 1, 'name': 'I'm parent', 'children': [ { '@id': '54f47be7-af77-9a5a-8606-a1e6df299eu8', 'id': 10, 'name': 'I'm first child', 'parent': '44f47be7-af77-9a5a-8606-a1e6df299ec9' }, { '@id': '98c47be7-af77-9a5a-8606-a1e6df299c7a', 'id': 11, 'name': 'I'm second child', 'parent': '44f47be7-af77-9a5a-8606-a1e6df299ec9' }, { '@id': '5jo47be7-af77-9a5a-8606-a1e6df2994g2', 'id': 11, 'name': 'I'm third child', 'parent': '44f47be7-af77-9a5a-8606-a1e6df299ec9' } ], 'filteredChildren': [ '54f47be7-af77-9a5a-8606-a1e6df299eu8', '5jo47be7-af77-9a5a-8606-a1e6df2994g2' ] }

filteredChildrenに示すように配列の場合、参照されるオブジェクトとそのコンテンツのレプリカではなく、JSONにオブジェクト参照を含めることができます。

要約

このソリューションを使用すると、オブジェクトとデータへの制約を最小限に抑える方法でJSONファイルをシリアル化しながら、循環参照関連の例外を排除できます。 JSONファイルのシリアル化を処理するために使用しているライブラリでそのようなソリューションがまだ利用できない場合は、提供されている実装例に基づいて独自のソリューションを実装できます。これがお役に立てば幸いです。

Unity with MVC:ゲーム開発をレベルアップする方法

バックエンド

Unity with MVC:ゲーム開発をレベルアップする方法
次の傾向:オマージュとデザインの盗用

次の傾向:オマージュとデザインの盗用

Uiデザイン

人気の投稿
国際的に拡張する方法:グローバルな製品設計
国際的に拡張する方法:グローバルな製品設計
電子メール感情分析ボットを構築する方法:NLPチュートリアル
電子メール感情分析ボットを構築する方法:NLPチュートリアル
Gulp:サイトの速度を最大化するためのWeb開発者の秘密兵器
Gulp:サイトの速度を最大化するためのWeb開発者の秘密兵器
BlackBerryに何が起こったのか:ゾンビストックまたはカムバックキング?
BlackBerryに何が起こったのか:ゾンビストックまたはカムバックキング?
グラス・スティーガル法:その廃止は金融危機を引き起こしたか?
グラス・スティーガル法:その廃止は金融危機を引き起こしたか?
 
非従来型のデータストレージに関するデータエンジニアガイド
非従来型のデータストレージに関するデータエンジニアガイド
iOSで無限ランナーを構築する方法:Cocos2D、自動化など
iOSで無限ランナーを構築する方法:Cocos2D、自動化など
Unity with MVC:ゲーム開発をレベルアップする方法
Unity with MVC:ゲーム開発をレベルアップする方法
正確な財務モデルを構築するためのステップバイステップガイド
正確な財務モデルを構築するためのステップバイステップガイド
PhalconPHP:高負荷のRESTfulAPIのソリューション
PhalconPHP:高負荷のRESTfulAPIのソリューション
人気の投稿
  • cとc ++を学ぶ
  • データ分析および視覚化ツール
  • cで何ができますか
  • 最も一般的なWebアプリケーション攻撃を3つ挙げてください
  • Javaでメソッドをテストする方法
  • モバイルユーザーインターフェースの設計原則
  • node.jsサーバーサイドJavaScriptとは
カテゴリー
計画と予測 ツールとチュートリアル 投資家と資金調達 技術 トレンド アジャイル プロセスとツール 製品ライフサイクル ヒントとツール 財務プロセス

© 2021 | 全著作権所有

apeescape2.com