【问题标题】:XStream: problems with Generic LinkedHashMapXStream:通用 LinkedHashMap 的问题
【发布时间】:2016-03-07 16:20:32
【问题描述】:

我正在使用 XStream 1.4.8 并尝试序列化通用 LinkedHashMap<?,?>

LinkedHashMap 在被 XStream 序列化时似乎不会保持其顺序。我需要为其编写一个新的转换器。

由于我有几种不同类型的通用类 LinkedHashMap 正在使用中,我希望只需要一个适用于通用版本的转换器。

换句话说: 给定一个要序列化的任意对象,该对象可能包含几种不同类型的LinkedHashMap<?,?> 类型的字段,您如何对它们进行编组和解组,每个泛型使用正确的类型,以及在每个泛型中维护的顺序LinkedHashMaps?

这个问题类似,但不是针对泛型的,也是基于旧版本的 XStream: Xstream does not maintain order of child elements while unmarshalling

【问题讨论】:

    标签: java xstream linkedhashmap


    【解决方案1】:

    你可以在这个源代码中得到一些想法:

    https://github.com/bluesoft-rnd/aperte-workflow-core/blob/master/core/activiti-context/src/main/java/org/aperteworkflow/ext/activiti/ActivitiStepAction.java#L47

        Map params = new HashMap();
        if (this.params != null) {
            String xml = (String) this.params.getValue(execution);
            if (xml != null) {
                XStream xs = new XStream();
                xs.alias("map", java.util.Map.class);
                xs.registerConverter(new Converter() {
                    public boolean canConvert(Class clazz) {
                        return AbstractMap.class.isAssignableFrom(clazz);
                    }
    
                    public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
                        AbstractMap<String, String> map = (AbstractMap<String, String>) value;
                        for (Map.Entry<String, String> entry : map.entrySet()) {
                            writer.startNode(entry.getKey().toString());
                            writer.setValue(entry.getValue().toString());
                            writer.endNode();
                        }
                    }
    
                    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
                        Map<String, String> map = new HashMap<String, String>();
    
                        while (reader.hasMoreChildren()) {
                            reader.moveDown();
                            map.put(reader.getNodeName(), reader.getValue());
                            reader.moveUp();
                        }
                        return map;
                    }
                });
                params = (Map) xs.fromXML(xml);
            }
        }
    

    【讨论】:

    • 这仅在 Map 始终为 类型时才有效。通用案例呢?
    【解决方案2】:

    这里有一些例子:

    /**
     * Checks whether the XStream-based REST converters should be used for
     * rendering the response of this REST request.
     * <p>
     * This method checks whether the request has a query attribute named
     * <tt>"rest"</tt>, or whether there is a system-wide
     * {@link BundleProperties#getProperty(String) bundle/system property}
     * named <tt>"skalli.rest"</tt> with a value <i>different</i> from <tt>"v1"</tt>.
     * In that case, e.g. the request has a query attribute <tt>"rest=v2"</tt>,
     * the method returns <code>false</code> to indicate that the new
     * RestWriter-based converters should be employed. Otherwise the
     * method returns <code>true</code>.
     * <p>
     * If the requested media type is different from <tt>"text/xml"</tt>,
     * always <code>false</code> will be returned.
     *
     * @return <code>true</code>, if XStream-based converters should be used
     * for rendering the response of this REST request.
     */
    @SuppressWarnings("nls")
    protected boolean enforceOldStyleConverters() {
        if (!context.isXML()) {
            return false;
        }
        String restVersion = getQueryAttribute("rest");
        if (StringUtils.isBlank(restVersion)) {
            restVersion = BundleProperties.getProperty("skalli.rest");
        }
        if (StringUtils.isNotBlank(restVersion)) {
            return "v1".equalsIgnoreCase(restVersion);
        }
        return true;
    }
    

    完整的源代码在这里:

    http://code.openhub.net/file?fid=FMrVl1G9kYhg416Lk5dachOp98c&cid=br-mQGOdySQ&s=XStream%3A%20problems%20with%20Generic%20LinkedHashMap&pp=0&fl=Java&ff=1&filterChecked=true&fp=390342&mp,=1&ml=1&me=1&md=1&projSelected=true#L0

    【讨论】:

    • 我不明白这与 LinkedHashMaps 或处理泛型有什么关系。您的链接似乎只是将我的问题输入搜索引擎的结果,与此无关。
    【解决方案3】:

    经过反复试验,我找到了解决方案。对于任何感兴趣的人,这里是:

    事实证明,泛型类实际上并没有我担心的那么严重。具有讽刺意味的是,通常令人讨厌的 Java 编译时解释最终对我们有利:实际上,创建一个只有 Object 作为泛型类型的 LinkedHashMap 就足够了。

    但是,确实需要注意确保存储在地图中的对象被解组为正确的类。这可以很容易地完成,只需为地图的每个条目存储类信息。请注意,仅将整个地图的类信息存储一次是不够的,因为地图的某些条目可能是该类的子类。

    private static class LinkedHashMapConverter implements Converter {
        @SuppressWarnings("rawtypes")
        @Override
        public boolean canConvert(Class clazz) {
            return clazz.equals(LinkedHashMap.class);
        }
    
        @Override
        public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
            @SuppressWarnings("unchecked")
            LinkedHashMap<Object, Object> map = (LinkedHashMap<Object, Object>) value;
            // store each entry
            for (Entry<Object, Object> a : map.entrySet()) {
                writer.startNode("entry");
                // store the key, the value, and the types of both
                writer.startNode("keyClass");
                context.convertAnother(a.getKey().getClass());
                writer.endNode();
                writer.startNode("key");
                context.convertAnother(a.getKey());
                writer.endNode();
                writer.startNode("valueClass");
                context.convertAnother(a.getValue().getClass());
                writer.endNode();
                writer.startNode("value");
                context.convertAnother(a.getValue());
                writer.endNode();
                writer.endNode();
            }
        }
    
        @SuppressWarnings("rawtypes")
        @Override
        public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
            LinkedHashMap<Object, Object> res = new LinkedHashMap<Object, Object>();
            while (reader.hasMoreChildren()) {
                reader.moveDown();
                // load the key, the value, and the types of both
                reader.moveDown();
                Class keyClass = (Class) context.convertAnother(res, Class.class);
                reader.moveUp();
                reader.moveDown();
                Object key = context.convertAnother(res, keyClass);
                reader.moveUp();
                reader.moveDown();
                Class valueClass = (Class) context.convertAnother(res, Class.class);
                reader.moveUp();
                reader.moveDown();
                Object value = context.convertAnother(res, valueClass);
                reader.moveUp();
                res.put(key, value);
                reader.moveUp();
            }
            return res;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-08-07
      • 1970-01-01
      • 2013-04-22
      • 1970-01-01
      • 2013-04-02
      • 1970-01-01
      • 1970-01-01
      • 2021-05-17
      • 1970-01-01
      相关资源
      最近更新 更多