代码之家  ›  专栏  ›  技术社区  ›  leoll2

泛型类的XStream反序列化返回java。lang.对象

  •  0
  • leoll2  · 技术社区  · 6 年前

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

    让我们看看这个例子,它使解释更容易。我使用泛型类和非泛型类来比较它们的行为。

    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;
    }
    

    非通用类。java:

    public class NonGenericClass {
        public double myNonGenericField;
    }
    

    盒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();
        }
    }
    

    主要的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。朗。Object@2d928643

    myNonGenericField=20.3

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

    myGenericField=20.3

    myNonGenericField=20.3

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

    我怀疑XStream处理泛型类型的方式可能存在问题,但我不确定。 一般来说,我对XStream和XML技术没有太多的经验,因此我非常希望能通过工作代码/示例给出答案,而不是“您需要实现XYZ,在这个混乱的教程中了解如何实现!”。

    非常感谢你!

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

    2 回复  |  直到 6 年前
        1
  •  1
  •   user158037    6 年前

    没有 好的 用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作为字段/成员的类编写一个单独的转换器。请注意,当您使用XStream封送此类对象时,它会生成:

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

    以及额外的 class="double" 是XStream无法自行推断的。

        2
  •  1
  •   tom    6 年前

    这里的问题是 TypeErasure 尤其地

    如果类型参数是无界的,则用其边界或对象替换泛型类型中的所有类型参数。因此,生成的字节码只包含普通类、接口和方法。

    所以

    public T myGenericField;
    

    成为

    public Object myGenericField; 
    

    在运行时/字节码中。

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

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

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