XStream 泛型的反序列化 class returns java.lang.Object

XStream deserialization of generic class returns java.lang.Object

我正在努力将一些 XML 转换为通用 classes 的对象。我的代码适用于非泛型 classes,但不适用于泛型。

我们来看这个例子,这样更容易解释。我同时使用通用和非通用 classes 来比较它们的行为。

Xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<Box>
    <doubleFieldA>
        <myGenericField>20.3</myGenericField>
    </doubleFieldA>
    <doubleFieldB>
        <myNonGenericField>20.3</myNonGenericField>
    </doubleFieldB>
</Box>

Xsd 架构:

<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Box">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="doubleFieldA">
          <xs:complexType>
            <xs:sequence>
              <xs:element type="xs:double" name="myGenericField"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="doubleFieldB">
          <xs:complexType>
            <xs:sequence>
              <xs:element type="xs:double" name="myNonGenericField"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
</xs:element>

GenericClass.java:

public class GenericClass<T> {
    public T myGenericField;
}

非GenericClass.java:

public class NonGenericClass {
    public double myNonGenericField;
}

Box.java:

import com.thoughtworks.xstream.XStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Box {
    public GenericClass<Double> doubleFieldA;
    public NonGenericClass doubleFieldB;

    private void deserialize() {
        Box b;
        String s;
        XStream xs = new XStream();
        try{
            s = new String(Files.readAllBytes(Paths.get("src/my_xml.xml")));
            b = (Box)xs.fromXML(s);
            doubleFieldA = b.doubleFieldA;
            doubleFieldB = b.doubleFieldB;
        } catch (IOException ioe) {
            System.err.println("IOException: " + ioe.getMessage());
        }
    }

    public Box() {
        deserialize();
    }
}

Main.java:

public class Main {

    public static void main(String[] args) {
        Box box = new Box();
        System.out.println("myGenericField = " + box.doubleFieldA.myGenericField);
        System.out.println("myNonGenericField = " + box.doubleFieldB.myNonGenericField);
    }
}

输出:

myGenericField = java.lang.Object@2d928643

myNonGenericField = 20.3

预期输出(我想要的!):

myGenericField = 20.3

myNonGenericField = 20.3

我有一个具有两个属性的 Box class:一个是通用 class,另一个只是普通 class。
两者都有一个属性,在此特定示例中其类型为 double。
我尝试使用 XStream.
使用存储在 XML 文件中的值(两者都是 20.3)来初始化它们 最终,当我打印这些属性时,我得到了非泛型 class 的正确值。相反,通用 class 属性会导致 java.lang.Object.
我应该如何修改我的代码以获得预期的行为?

我怀疑问题可能出在 XStream 处理泛型类型的方式上,但我不确定。 一般来说,我对 XStream 和 XML 技术没有太多经验,所以我非常感谢使用 code/examples 而不是 "you need to implement XYZ, find out how in this messy tutorial!" 的答案。

非常感谢!

注意:此代码的唯一目的是显示我的问题。我在一个更大的项目中遇到过这个问题,所以这只是一个重现它的小例子。

没有 好的 方法可以用 XStream 做到这一点,它甚至不知道泛型的存在。您可以添加自定义转换器:

public static class BoxConverter implements Converter {

    public boolean canConvert(Class clazz) {
            return clazz.equals(Box.class);
    }

    public void marshal(Object value, HierarchicalStreamWriter writer,
                    MarshallingContext context) {
            throw new RuntimeException("to do");
    }

    public Object unmarshal(HierarchicalStreamReader reader,
                    UnmarshallingContext context) {
            Box box = new Box();
             while (reader.hasMoreChildren()) {
                    reader.moveDown();
                    if ("doubleFieldA".equals(reader.getNodeName())) {
                            reader.moveDown();
                            Double val = Double.valueOf(reader.getValue());
                            reader.moveUp();
                            GenericClass<Double> genericObject = new GenericClass<>();
                            genericObject.myGenericField = val;
                            box.doubleFieldA = genericObject;
                    } else if ("doubleFieldB".equals(reader.getNodeName())) {
                            box.doubleFieldB =(NonGenericClass)context.convertAnother(box, NonGenericClass.class);
                    }
                    reader.moveUp();
            }
            return box;
    }
}

并注册它:

XStream xs = new XStream();
xs.registerConverter(new BoxConverter());
Box b = (Box) xs.fromXML(input);

但这需要为每个具有 GenericClass 作为 field/member 的 class 编写一个单独的转换器。请注意,当您使用 XStream 编组此类对象时,它会生成:

<Box>
  <doubleFieldA>
    <myGenericField class="double">20.3</myGenericField>
  </doubleFieldA>
  <doubleFieldB>
    <myNonGenericField>20.3</myNonGenericField>
  </doubleFieldB>
</Box>

并且 class="double" 是 XStream 无法自行推断的。

这里的问题是 TypeErasure,尤其是

Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.

所以

public T myGenericField;

变成

public Object myGenericField; 

在 runtime/in 字节码。

所以 XStream 无法确定 myGenericField 应该是双精度的...因此它被反序列化为 Object 实例。

java.lang.Object@2d928643 只是 myGenericField.toString()

的输出

所以,我看到的唯一方法是创建一个 custom converter