【问题标题】:How to unmarshal json to java.util.Map<String, Object> with MOXy?如何使用 MOXy 将 json 解组为 java.util.Map<String, Object>?
【发布时间】:2014-11-26 01:27:47
【问题描述】:

我知道有类似的问题,例如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 时遇到问题。 “数据”字段没有特定的模式,因此它可以包含数组、键值对或任何其他有效的 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)

【问题讨论】:

  • 好的,我切换到杰克逊,它工作得很好。 @XmlJavaTypeAdapter@XmlElementWrapper 甚至不需要解组 List 和 Map

标签: java json jaxb moxy


【解决方案1】:

也许您应该尝试创建正确的 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”的拼写有点错误:-)

【讨论】:

  • 感谢塞巴斯蒂安,这些错别字已修复:) 我尝试了您的建议,但得到了同样的例外。我认为 JAXBContext 在我的代码中是正确创建的。如果我删除了“映射”部分,它可以解组字符串键值对和列表。当我尝试将 json 解组为 Map 值时,它只会抛出异常。
  • unmarshalling 期间,我也遇到了类似的问题。我正在尝试将 unmarshal 未知元素添加到 Map&lt;String,Object&gt; 中,但它没有按预期工作。我在下面提供的链接中发布了完整的问题。如果你有机会可以看看它并提供你的建议:stackoverflow.com/questions/67648941/…
猜你喜欢
  • 2011-03-08
  • 2017-09-28
  • 2015-03-29
  • 2022-11-19
  • 2020-09-25
  • 1970-01-01
  • 1970-01-01
  • 2011-11-16
  • 2019-09-04
相关资源
最近更新 更多