2016-03-07 3 views
1

Я использую XStream 1.4.8 и пытаюсь сериализовать общий LinkedHashMap<?,?>.XStream: проблемы с Generic LinkedHashMap

Возможно, LinkedHashMap не выполнил свой заказ при сериализации XStream. Мне нужно написать для него новый конвертер, чтобы он это сделал.

Проблема усугубляется тем фактом, что у меня есть несколько разных типов используемого класса LinkedHashMap, и мне нужен только один конвертер, который работает для общей версии.

Другими словами: дал такое а произвольный объект сериализации, который может содержать несколько различных видов полей типа LinkedHashMap<?,?>, как вы маршала и распаковать их всех, с правильными типами используемых для каждого из дженериков, и с порядок поддерживается в каждой из LinkedHashMaps?

Этот вопрос похож, но это не для дженериков, а также на базе старой версии XStream: Xstream does not maintain order of child elements while unmarshalling

ответ

0

Вы можете получить некоторые идеи в этом исходном коде:

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); 
     } 
    } 
+0

Это работает, только если Карта всегда имеет тип . Как насчет общего случая? –

0

Некоторые примеры здесь:

/** 
* 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

+0

Я не понимаю, что это касается как LinkedHashMaps, так и обработки дженериков. Ваша ссылка, похоже, является результатом ввода моего вопроса в поисковую систему и не имеет к этому никакого отношения. –

0

После нескольких проб и ошибок, я нашел решение. Для всех, кого это интересует, вот оно:

Оказывается, что классы, являющиеся общими, на самом деле не такие большие проблемы, как я боялся. По иронии судьбы, интерпретация времени компиляции Java, которая, как правило, вызывает раздражение, в конечном итоге приносит нам пользу: на самом деле достаточно создать 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; 
    } 
}