添加XMLAdapter会导致JAXBContext.newInstance方法中出现NullPointerException异常

问题描述:

我正在学习JAXB并希望尝试一些适配器。当我在我的非常简单的类中添加一个时,它导致JAXBContext.newInstance()调用抛出NullPointerException。请注意,适配器并非绝对必要。如果我注释掉@XmlJavaTypeAdapter(MapTypeAdaptor.class)注释,代码将起作用。但是这并不能帮助我学习如何使用适配器......将MapType.class和MapTypeEntry.class添加到JAXBContext.getInstance()也没有解决问题。添加XMLAdapter会导致JAXBContext.newInstance方法中出现NullPointerException异常

我真的很感激任何意见,我做错了什么。谢谢!

这里的Java对象我编组:

import java.util.List; 
import java.util.Map; 
import javax.xml.bind.annotation.XmlRootElement; 
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 
import com.s5a.api.models.jaxb.MapTypeAdaptor; 

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlRootElement 
public class TestCollections { 

@XmlJavaTypeAdapter(MapTypeAdaptor.class) // <---Adding this line causes the error 
private Map<String, Object> oneColor; 
public Map<String, Object> getColor() { 
    return oneColor; 
} 
public void setColor(Map<String, Object> oneColor) { 
    this.oneColor = oneColor; 
} 

public List<String> getListOfColors() { 
    return listOfColors; 
} 

public void setListOfColors(List<String> listOfColors) { 
    this.listOfColors = listOfColors; 
} 

private List<String> listOfColors; 


} 

这里的适配器:

import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.Map; 
import java.util.Map.Entry; 
import javax.xml.bind.annotation.adapters.XmlAdapter; 

public class MapTypeAdaptor extends XmlAdapter<MapType, Map<String, Object>> { 

@Override 
public MapType marshal(Map<String, Object> map) throws Exception { 
    ArrayList<MapEntryType> entries = new ArrayList<MapEntryType>(); 
    MapType mapType = new MapType(); 
    if (map != null && map.entrySet() != null){ 
     for (Entry<String, Object> entry : map.entrySet()) { 
      MapEntryType mapEntryType = new MapEntryType(); 
      if (entry != null){ 
       mapEntryType.setKey(entry.getKey()); 
       mapEntryType.setValue(entry.getValue()); 
      } 
      entries.add(mapEntryType); 
     } 
     mapType.setEntries(entries); 
    } 
    return mapType; 
} 

@Override 
public Map<String, Object> unmarshal(MapType map) throws Exception { 
    HashMap<String, Object> hashMap = new HashMap<String, Object>(); 
    if (map != null){ 
     for (MapEntryType entryType : map.getEntries()) { 
      if (entryType != null){ 
       hashMap.put(entryType.getKey(), entryType.getValue()); 
      } 
     } 
    } 
    return hashMap; 
} 
} 

这里的地图类型类:

import java.util.ArrayList; 
import java.util.List; 

public class MapType { 

private List<MapEntryType> mapEntries = new ArrayList<MapEntryType>(); 

public List<MapEntryType> getEntries() { 
    return mapEntries; 
} 

public void setEntries(List<MapEntryType> mapEntries) { 
    this.mapEntries = mapEntries; 
} 
} 

而这里的MapEntryType类:

import javax.xml.bind.annotation.XmlAccessType; 
import javax.xml.bind.annotation.XmlAccessorType; 
import javax.xml.bind.annotation.XmlAttribute; 
import javax.xml.bind.annotation.XmlValue; 

@XmlAccessorType(XmlAccessType.FIELD) 
public class MapEntryType { 

@XmlAttribute 
private String key; 
@XmlValue 
private Object value; 

public String getKey() { 
    return key; 
} 

public void setKey(String key) { 
    this.key = key; 
} 

public Object getValue() { 
    return value; 
} 

public void setValue(Object value) { 
    this.value = value; 
} 
} 

最后,我的单元测试:

@XmlAttribute 
private String key; 
@XmlValue 
private Object value; 

到:

@Test 
    public void shouldReturnXMLRepresentation() throws Exception { 

    TestCollections test = new TestCollections(); 
    HashMap<String, Object> color1 = new HashMap<String, Object>(); 
    color1.put("blue", new Integer(50)); 
    HashMap<String, Object> color2 = new HashMap<String, Object>(); 
    color2.put("red", "red is the 2nd color"); 
    ArrayList<Map<String, Object>> colors = new ArrayList<Map<String, Object>>(); 
    colors.add(color1); 
    colors.add(color2); 
    test.setColor(color1); 

    ArrayList<String> listofstrings = new ArrayList<String>(); 
    listofstrings.add("foo"); 
    listofstrings.add("bar"); 
    test.setListOfColors(listofstrings); 

    String xmlRepresentaion = genXML(test); 
    assertTrue(xmlRepresentaion != null); 
    assertTrue(xmlRepresentaion.length() > 0); 
} 

private String genXML(Object object) throws Exception{ 
StringWriter writer = new StringWriter(); 
try { 
      /* I tried the following, but it also throw an NPE 
      JAXBContext jc = JAXBContext.newInstance(TestCollections.class, MapType.class, MapTypeEntry.class); 
      */ 
    JAXBContext jc = JAXBContext.newInstance(TestCollections.class); //<-- NPE 
    Marshaller marshaller = jc.createMarshaller(); 
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
    marshaller.marshal(object, writer); 
}catch (Exception e){ 
    System.out.println("Error marshalling: " + e.getMessage()); 
    e.printStackTrace(System.out); 
    throw e; 
} 
System.out.println(writer.toString()); 
    return writer.toString(); 
} 
+0

我注意到,JAXBExceptions有一个“唯一”格式,其中e.toString()为您提供e.getMessage()不包含的信息。 NPE几乎肯定是由其他一些错误引起的;有人没有一个没有参数的构造函数或其他东西。 – kylewm 2012-03-01 01:36:56

+0

谢谢凯尔,这是很好的知道,但在这种情况下没有提供任何新的信息。 toString()方法返回相同的NPE,没有附加消息。 – rzrelyea 2012-03-01 14:14:13

+0

我遇到了同样的问题... Map的对象类型似乎是问题所在。更改为映射修复了问题。但是,我仍然在寻找一个解决方案,因为我想透明地序列化各种类型,如字符串,数字,日期。 – Raman 2012-05-02 01:49:44

如果需要存储对象的值,并愿意接受一个稍微不同的XML输出,可以从改变MapEntryType:

@XmlElement 
private String key; 
@XmlElement 
private Object value; 

这将产生输出,如:

<entry> 
    <key>someKey</key> 
    <value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">someValue</value> 
</entry> 

代替:

<entry key="someKey">someValue</entry> 

另外,如果你可以改变你的地图从Map<String, Object>Map<String, String>,那么你现有的类应该工作。