0

次のようなオブジェクトグラフがあります。

root
    : childs (array)
        : childs (array)

これからJSON応答を構築しているので、各コレクションをループして次のようなコードを作成する必要があります。

// code for root

// loop through direct root childs
for (Child child : childs) {

    // Loop through the childs of the object in current context.
    for (AnotherChild anotherChild : moreChilds) {

    }
}

そのようなコードをどのように回避しますか?最終的には矢印になります。forループのレベルごとに独自のメソッドを作成することもできますが、それは良いアプローチですか?より良い他のアプローチはありますか?

4

4 に答える 4

3

この特定の問題 (JSON 応答の作成) について話している場合は、 jacksonのようなある種のシリアライザーを使用するか 、カスタムのものを作成します。このトピックに関連する質問があります https://stackoverflow.com/questions/338586/a-better-java-json-library

一方、他の用途では、GuavaLambdajなどのより機能的なアプローチを使用できます。

しかし、それが非常に複雑になると、これらはあまり役に立たないので、可能であれば別のアプローチを試してみてください.

于 2012-11-18T00:14:20.577 に答える
2

これは再帰的な構造であるため、ネストを処理するには再帰を使用する必要があります。深さ優先探索を行う必要があります。

JSONをインターフェースするように編集すると、再帰的な訪問の擬似コードのサンプルについて、@MiteMitreskiのアドバイスに従うことになります。

void visit(Child tree) {
  json_write_class(tree);
  for (Attribute a : tree.attributes) {
    json_write_attr(a);
  if (tree.children != null) {
    json_push_indent();
    for (Child child : tree.children) {
      visit(child);
    }
    json_pop_indent();
  }
}

より詳細な制御が必要な場合は、そのツリーのノードに一種の「セマンティックアクション」を記述して属性を確立し、ビジターパターンを実装してデータを出力できます(最初の選択肢よりも詳細)。

多くの場合、文法と構文木のアナロジーを使用するのに役立ちます。これらは、(プログラマーとしての)私たちが慣れている最も明白なサンプルです。

于 2012-11-18T00:00:10.017 に答える
1

これらすべてのループを実行しているクラスは他のクラスの地獄を知っているので(したがって、デメテルの法則を破っているので)、そこには厄介な設計上の問題があると思います。

私が使用しようとしているアプローチ(非常に経験豊富な開発者から学んだこと)は、コレクション(または配列)を独自のクラスでラップすることです。次に、1つの操作を実行する配列/コレクションを反復処理するメソッドを作成します。この場合、コレクションをラップする別のクラスの別のメソッドを呼び出す可能性があります。

このように、各クラスは、他のクラスが何をするか(または子オブジェクトの内部)についてほとんど知識がありません。


編集

これが例です。あなたがアマゾンに似たウェブサイトにアカウントを持っていると想像してください。そのアカウントでは、いくつかのクレジットカードを関連付けています。

だから、持っている代わりに

class Account {
    List<CreditCard> creditCards;

    public CreditCard getPrimaryCard() {
        //complex code to find the primary credit card
    }
    //lots of other code related to the account and credit cards
}

できるよ

class Account {
    CreditCards creditCards;

    public CreditCard getPrimaryCard() {
        creditCards.getPrimaryCard()
    }
    //lots of other code related to the account
}

class CreditCards {
    List<CreditCard> creditCards;

    public CreditCard getPrimaryCard() {
        //complex code to find the primary credit card
    }
    public void addCard(CreditCard creditCard) {
        //complex logic to validate that the card is not duplicated.
    }
    //lots of other code related to credit cards
}

このように、アカウントはクレジットカードがメモリにどのように保存されているかを知る必要はありません(リストかセットか、それともリモートWebサービスから取得するか)。

これは些細な例であることに注意してください。

于 2012-11-17T23:59:32.747 に答える
1

関心のあるすべてのクラスが実装する必要があるインターフェイスを提供できます。そのインターフェースは、現在のオブジェクトを JSON に変換する方法を提供する必要があります。例を参照してください:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class JsonProgram {

    public static void main(String[] args) {
        Root root = new Root(Arrays.asList(new Child(Arrays.asList(
                new AnotherChild(1), new AnotherChild(2)))));
        System.out.println(root.toJSON());
    }

}

interface JsonState {
    String toJSON();
}

class Root implements JsonState {

    private List<Child> childs = new ArrayList<Child>();

    public Root(List<Child> childs) {
        this.childs = childs;
    }

    @Override
    public String toJSON() {
        StringBuilder builder = new StringBuilder();
        builder.append("{").append("\"childs\"").append(":[");
        int index = 0;
        for (Child child : childs) {
            builder.append(child.toJSON());
            if (index < childs.size() - 1) {
                builder.append(",");
            }
            index++;
        }
        builder.append("]\"}");
        return builder.toString();
    }
}

class Child implements JsonState {

    private List<AnotherChild> anotherChilds = new ArrayList<AnotherChild>();

    public Child(List<AnotherChild> anotherChilds) {
        this.anotherChilds = anotherChilds;
    }

    @Override
    public String toJSON() {
        StringBuilder builder = new StringBuilder();
        builder.append("{").append("\"anotherChilds\"").append(":[");
        int index = 0;
        for (AnotherChild child : anotherChilds) {
            builder.append(child.toJSON());
            if (index < anotherChilds.size() - 1) {
                builder.append(",");
            }
            index++;
        }
        builder.append("]}");
        return builder.toString();
    }
}

class AnotherChild implements JsonState {

    private int value;

    public AnotherChild(int value) {
        this.value = value;
    }

    @Override
    public String toJSON() {
        StringBuilder builder = new StringBuilder();
        builder.append("{").append("\"value\"").append(":\"").append(value)
                .append("\"}");
        return builder.toString();
    }
}

出力:

{
   "childs":[
      {
         "anotherChilds":[
            {
               "value":"1"
            },
            {
               "value":"2"
            }
         ]
      }
   ]
}

しかし、それは良い解決策ではありません。独自のソリューションを実装する代わりに、それを実行できるライブラリを使用する必要があります。google-gsonをお勧めします。私にとっては最高です。

編集 - GSONの例

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class JsonProgram {

    public static void main(String[] args) {
        Root root = new Root(Arrays.asList(new Child(Arrays.asList(
                new AnotherChild(1), new AnotherChild(2)))));

        Gson gson = new GsonBuilder().serializeNulls().create();
        System.out.println(gson.toJson(root));
    }
}

class Root {

    private List<Child> childs = new ArrayList<Child>();

    public Root(List<Child> childs) {
        this.childs = childs;
    }

    @Override
    public String toString() {
        return Arrays.toString(childs.toArray());
    }
}

class Child {

    private List<AnotherChild> anotherChilds = new ArrayList<AnotherChild>();

    public Child(List<AnotherChild> anotherChilds) {
        this.anotherChilds = anotherChilds;
    }

    @Override
    public String toString() {
        return Arrays.toString(anotherChilds.toArray());
    }
}

class AnotherChild {

    private int value;

    public AnotherChild(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return Integer.toString(value);
    }
}

上記の例は同じ出力を作成します。私にとって、これはよりエレガントなソリューションです。

于 2012-11-18T00:25:24.670 に答える