5

我有几种情况,其中一个类由任意(但固定)数量的不同类型的变量组成,并希望迭代这些变量并在代码中使用它们的名称。有没有比在每个函数中重新键入每个变量名更聪明的方法呢?

我最初的想法是使用 HashMap 来存储局部变量,但这似乎效率低下并且不能处理多种类型。

模拟下面的代码以缩短所需的读数,但通常它的变量超过 3 个:

class Widget implements Parcelable {
    String name, code;
    Long price;

    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeString(code);
        dest.writeLong(price);
    }

    public Widget(Parcel o) {
        name = o.name;
        code = o.code;
        price = o.price;
    }

    public String getXML() {
        return "<Widget><name>"+name+"</name><code>"+code
              +"</code><price>"+price+"</price></Widget>";
    }
}

相反,我更喜欢做类似(伪代码)的事情:

public Widget(Parcel o) {
    for (Map<Name,Data> item: o.getMap()) {
        this.setValue(Name, Data);
    }
}
public String getXML() {
    StringBuilder XML = new StringBuilder();
    XML.append("<Widget>");
    for (Map<Name,Data> item: o.getMap()) {
        XML.append("<"+Name+">" + Data + "</"+Name+">");
    }
    XML.append("</Widget>");
    return XML.toString();
}

我一直认为必须有一种模式来做这种事情,但也许这只是我的 Python 历史对我的 Java 经验的影响。

更新:这不仅仅是从成员变量中获取 XML,而是在多个成员函数中循环访问成员变量。看来我的曲目中缺少 Java 的反射功能。

我新的较小的构造函数让我很高兴学到了一些有用的东西,看起来像:

public Widget(Widget other) {
    for (Field f : getClass().getDeclaredFields()) {
        if (f.getName() == "TAG") {
            continue;
        }
        f.set(this, f.get(other));
    }
}
4

4 回答 4

13

这通常通过反射解决:

for (Field f : getClass().getDeclaredFields()) {
    String name = f.getName();
    String value = f.get(this);
}

但是,对于写入/读取 XML,您可能希望使用JAXB而不是重新发明轮子。

于 2012-11-29T20:21:48.077 回答
3

使用杰克逊:

https://github.com/FasterXML/jackson-dataformat-xml

序列化的完成与 JSON 序列化非常相似:所有需要更改的只是要使用的 ObjectMapper 实例:

// Important: create XmlMapper; it will use proper factories, workarounds
ObjectMapper xmlMapper = new XmlMapper();
String xml = xmlMapper.writeValue(new Simple());

和 POJO 一样:

public class Simple {
    public int x = 1;
    public int y = 2;
}

你会得到类似的东西:

<Simple>
  <x>1</x>
  <y>2</y>
</Simple>

(除了默认输出不缩进:您可以使用标准杰克逊机制启用缩进)

从 XML 反序列化 POJO 与序列化类似,反序列化与 JSON 反序列化没有太大区别:

ObjectMapper xmlMapper = new XmlMapper();
Simple value = xmlMapper
   .readValue("<Simple><x>1</x><y>2</y></Simple>", Simple.class);

PS您也可以为此使用反射并使用Class.getDeclaredFields(). 但根据我的经验,很少有人需要这样做。有很多用于不同目的的 3rd 方开源库,通常仅使用它们是更好的解决方案(例如,在这种特殊情况下为 Jackson)

于 2012-11-29T20:24:34.660 回答
2

如果有许多类的代码遵循相同的模式,我会编写一个代码生成器来从某种定义文件生成 Java 源代码。然后你可以让你的生成器像你的第一个例子一样创建简单的代码,这更容易阅读和理解(比使用通用映射或反射或其他东西)。定义文件成为每种对象字段的“官方”来源。

由于您已经熟悉 Python,您的定义文件可以直接构建到 Python 脚本中以创建源代码。例如:

Objects = {
    "Widget": [
        (TYPE_STRING, "name"),
        (TYPE_STRING, "code"),
        (TYPE_LONG, "price"),
    ],
    # ...
}

通过适当的定义TYPE_STRING等,编写一些代码来生成 Java 源代码应该很简单。

于 2012-11-29T20:20:49.527 回答
0

我相信XStream是最简单的框架。参考两分钟教程,你会明白的。

它就像创建一个映射类一样简单,例如Widget具有所有成员属性和可能的​​对象嵌套,然后调用:

   XStream xstream = new XStream(new DomDriver());
   String xml = xstream.toXML(widgetObject);  // <-- Get XML String from object

   Widget widget = (Widget)xstream.fromXML(xml); //<-- Get object from XML
于 2012-11-29T20:25:26.973 回答