java - 如何使用 MOXy 将 json 解码为 java.util.Map<String, Object>?

标签 java json jaxb moxy

我知道周围有类似的问题,例如 How to marshal/unmarshal a Map<Integer, List<Integer>>?JAXB java.util.Map binding 。我还经常阅读 Blaise Doughan 的博客,尤其是这篇文章:http://blog.bdoughan.com/2013/03/jaxb-and-javautilmap.html并尝试尽可能遵循他的建议,但是我仍然无法成功解码 json 负载,非常感谢您的帮助。

要解码的 json 负载如下所示:

{
    "uri":"\\foo\\dosomthing",
    "action":"POST",
    "queryParameters":[
        "$filter=aaa",
        "$orderby=bbb"
    ],
    "requestData":{
        "data1":{
            "key1":"value1",
            "key2":"value2"
        },
        "ids":[
            "1234",
            "0294"
        ]
    }
}

我在将“数据”解码到 java.util.Map 时遇到问题。 “data”字段没有特定的架构,因此它可以包含数组、键值对或任何其他有效的 json 数据。我决定用 Map 来包装它。根据我的研究,我认为我需要 XmlAdapter 来正确转换数据。

这是我的代码:

Java 模式类:

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class CustomerRequest
{
    public CustomerRequest() {}

    public CustomerRequest(String uri, String action, List<String>
 queryParameters, Map<String, Object> reqeustData)
    {
        this.uri = uri;
        this.action = action;
        this.queryParameters = queryParameters;
        this.requestData = reqeustData;
    }

    public String getUri()
    {
        return uri;
    }

    public String getAction()
    {
        return action;
    }

    public List<String> getQueryParameters()
    {
        return Collections.unmodifiableList(queryParameters);
    }

    public Map<String, Object> getRequestData()
    {
        return Collections.unmodifiableMap(requestData);
    }

    @XmlElement
    private String uri;

    @XmlElement
    private String action;

    @XmlElementWrapper
    private List<String> queryParameters = new ArrayList<String>();

    @XmlPath(".")
    @XmlJavaTypeAdapter(StringObjectMapAdapter.class)
    private Map<String, Object> requestData = new HashMap<String, Object>();

}

XmlAdpater:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlValue;
import javax.xml.bind.annotation.adapters.XmlAdapter;

import org.eclipse.persistence.oxm.annotations.XmlVariableNode;

public class StringObjectMapAdapter extends 
XmlAdapter<StringObjectMapAdapter.AdaptedMap, Map<String, Object>> 
{

public static class AdaptedEntry
{
    @XmlTransient
    public String key;

    @XmlValue
    public Object value = new Object();
}
public static class AdaptedMap
{
    @XmlVariableNode("key")
    List<AdaptedEntry> entries = new ArrayList<AdaptedEntry>();
}

@Override
public AdaptedMap marshal(Map<String, Object> map) throws Exception 
{
    AdaptedMap adaptedMap = new AdaptedMap();

    for (Entry<String, Object> entry : map.entrySet())
    {
        AdaptedEntry adaptedEntry = new AdaptedEntry();
        adaptedEntry.key = entry.getKey();
        adaptedEntry.value = entry.getValue();
        adaptedMap.entries.add(adaptedEntry);
    }
    return adaptedMap;
}

@Override
public Map<String, Object> unmarshal(AdaptedMap adaptedMap) throws Exception 
{
    List<AdaptedEntry> adapatedEntries = adaptedMap.entries;
    Map<String, Object> map = new HashMap<String, Object>(adapatedEntries.size());

    for (AdaptedEntry adaptedEntry : adapatedEntries )
    {
        map.put(adaptedEntry.key, adaptedEntry.value);
    }

    return map;
}

}

最后是我的测试应用程序:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;

import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.testng.annotations.Test;

public class TestStringObjectMapAdapter {

    @Test
    public void testUnmarshalFromJson() throws Exception 
    {
        JAXBContext jc = JAXBContext.newInstance(CustomerRequest.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
        unmarshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
        StreamSource json = new StreamSource("test-data.json");
        CustomerRequest request= unmarshaller.unmarshal(json,   
            CustomerRequest.class).getValue();

        assert(request.getUri().equals("\\foo\\dosomthing"));
        assert(request.getAction().equals("POST"));
    }
}

然后,当测试应用程序运行时,会生成 java.lang.ClassCastException 异常:

FAILED: testUnmarshalFromJson
java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.DocumentImpl cannot be cast to org.w3c.dom.Element
    at org.eclipse.persistence.internal.oxm.XMLCompositeObjectMappingNodeValue.endSelfNodeValue(XMLCompositeObjectMappingNodeValue.java:468)
    at org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl.endDocument(UnmarshalRecordImpl.java:606)
    at org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl.endElement(UnmarshalRecordImpl.java:1084)
    at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:304)
    at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parseRoot(JSONReader.java:179)
    at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:125)
    at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:140)
    at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:857)
    at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:707)
    at org.eclipse.persistence.oxm.XMLUnmarshaller.unmarshal(XMLUnmarshaller.java:655)
    at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:301)
    at com.absolute.asb.urp.services.domain.TestStringObjectMapAdapter.testUnmarshalFromJson(TestStringObjectMapAdapter.java:21)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)

最佳答案

也许您应该尝试创建正确的 MoXY JAXBContext,例如:

    private static synchronized JAXBContext createJAXBContext() throws JAXBException {
        if(jc == null){
            jc = org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(new Class[] {CustomerReqeust.class}, null);
        }
        return jc;
    }

或者使用 http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html 中提到的另一种方式

顺便说一句,“CustomerReqeust”拼写有点错误:-)

关于java - 如何使用 MOXy 将 json 解码为 java.util.Map<String, Object>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27139820/

相关文章:

Java - System.out.printf 舍入 float

java - 如何从 AngularJS 发布到 SpringMVC

sql - 如何查询 postgres json 列中的嵌套数组?

javascript - 使用 Angular js 在 JSON 中显示 HTML 元素

java - 如何使用 jaxb 生成 xml 标签作为完整类名?

java - Eclipse e4 应用程序 - 包含在插件中时未找到 ContextFactory

java - 静态缓存 - 两个 "Instances"

java.lang.IndexOutOfBoundsException : Index: 1, 大小:1 在 Jidesoft DocumentPane 中

java - 如何用Gson反序列化子类?

java - 如何通过 maven-jaxb 从 XSD 生成仅必需的类