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

但是当涉及到大的 O 复杂性时,这些并没有太大的帮助,所以如果可能的话,你可能想尝试不同的方法。

于 2012-11-18T00:14:20.577 回答
2

那是一个递归结构,那么你应该使用递归来处理嵌套。应该进行深度首次访问。

编辑接口 JSON 你真的会遵循@Mite Mitreski 的建议,以获得递归访问伪代码示例:

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

我认为您在那里有一个令人讨厌的设计问题,因为执行所有这些循环的类知道很多其他类(因此违反了Demeter 法则)。

我尝试使用的一种方法(我从一些非常有经验的开发人员那里学到的)是将集合(或数组)包装在他们自己的类中;然后创建迭代执行一项操作的数组/集合的方法。在这种情况下,它可能正在调用另一个包装集合的类中的另一个方法。

这样,每个类对其他类做什么(或子对象的内部结构)知之甚少。


编辑

这是一个例子。想象一下,您在类似于亚马逊的网站上有一个帐户。在该帐户中,您关联了几张信用卡。

所以,而不是拥有

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
}

这样,Account 就不需要知道信用卡是如何存储在内存中的(应该是列表?还是集合?还是从远程 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 回答