用于泽西岛的(哈希)映射的序列化器?

2022-01-21 00:00:00 json rest java jersey

我正在尝试将以下负载发布到基于 Jersey 的 Web 服务:

I'm trying to POST the following payload to my Jersey-based web service:

{
    "firstname":"Jimmy",
    "lastname":"Johns",
    "addresses":
    [
        {
            "street":"19 Mayberry Drive",
            "city":"Mayberry",
            "state":"nc",
            "postalcode":"27043",
            "country":"us",
            "addresstype":1
        }
    ],
    "data":
    {
        "eyes":"blue",
        "hair":"brown",
        "sandwich":"roast beef"
    }
}

我的球衣代码:

@POST
public Response create( Person person )
{
    createBo( person );    <------- stopped here in debugger
    ...

在 Jersey 打电话给我的时候停止了,我看到 person 中的地址完全被我正在寻找的内容(上面的 JSON 中的内容)冲掉了.但是,我的数据元组不存在.我知道 Jersey 正在为 Address 调用我的无参数构造函数,并且它的设置器被调用,但我在夜间起床,至于 Jersey 可能会或可能不会尝试使用这些我的 JSON 中的随机(数据")元组.(我说随机"是因为在不同的调用中,这些可能是洞穴":深、暗"、山":高、宽"等.这是我界面的一部分.)

Stopped just as Jersey calls me, I see addresses in person flushed out with exactly what I'm looking for (what's in the JSON above). However, my data tuples aren't there. I know Jersey is calling my no-arg constructor for Address es and its setters are getting called, but I'm up in the night as far as what Jersey might or might not be trying to do with these random ("data") tuples in my JSON. (I say "random" because in a different invocation, these might be "cave":"deep, dark", "mountain":"high, wide", etc. This is part and parcel of my interface.)

为了充实我所说的内容,将这些 POJO 视为上述内容的上下文:

To flesh out what I'm talking about, consider these POJOs as context for the above:

@XmlAccessorType( XmlAccessType.FIELD )
@XmlRootElement
public class Person implements Serializable
{
    @XmlElement
    private List< Address > addresses = new ArrayList< Address >();

    @XmlElement
    private Map< String, String > data = new HashMap< String, String >();

    ...

@XmlRootElement
public class Address implements Serializable
{
    private String  street;
    private String  city;
    private String  state;
    private String  country;
    private String  postalcode;
    private Integer addresstype;
    ...

注意:我不能像做 Address 那样做随机元组,因为我事先并不知道键是什么(而我限制了 Address 到街道、城市等).

Note: I can't do the random tuples as I've done Address because I don't actually know beforehand what the keys will be (whereas I limit Address to street, city, etc.).

我需要的是某种用于泽西岛 HashMap 的魔法序列化程序,我似乎无法很好地解释文档以理解如何编写一个或解决这个问题,同时仍保持灵活性我的界面.

What I need is some kind of magic serializer for HashMaps in Jersey and I cannot seem to interpret the docs well enough to understand how to write one or work around this problem while still maintaining the flexibility of my interface.

我将不胜感激有关如何完成此操作的任何指示.

I would appreciate any indication as to how to accomplish this.

拉斯贝特曼

附:请注意,Java.util.Map to JSON Object withJersey/JAXB/Jackson 没有帮助,尽管它显示出很大的希望.

P.S. Note sadly that Java.util.Map to JSON Object with Jersey / JAXB / Jackson was not helpful, though it showed great promise.

推荐答案

注意:我是EclipseLink JAXB (MOXy) 领导和 JAXB (JSR-222) 专家组.

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.

如果您使用的是 MOXy,以下内容将起作用,并且应该与任何其他 JAXB 提供程序一起使用.这种方法使用 XmlAdapterjava.util.Map 转换为 org.w3c.dom.Element.

The following will work if you are using MOXy, and should work with any other JAXB provider. This approach converts the java.util.Map to an org.w3c.dom.Element using an XmlAdapter.

地图适配器

XmlAdapter 允许您将一个类的实例编组为另一个类的实例(请参阅:http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html).

An XmlAdapter allows you to marshal an instance of one class as an instance of another class (see: http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html).

package forum11353790;

import java.util.*;
import java.util.Map.Entry;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.*;
import org.w3c.dom.*;

public class MapAdapter extends XmlAdapter<Element, Map<String, String>> {

    private DocumentBuilder documentBuilder;

    public MapAdapter() throws Exception {
        documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
    }

    @Override
    public Element marshal(Map<String, String> map) throws Exception {
        Document document = documentBuilder.newDocument();
        Element rootElement = document.createElement("data");
        document.appendChild(rootElement);
        for(Entry<String,String> entry : map.entrySet()) {
            Element childElement = document.createElement(entry.getKey());
            childElement.setTextContent(entry.getValue());
            rootElement.appendChild(childElement);
        }
        return rootElement;
    }

    @Override
    public Map<String, String> unmarshal(Element rootElement) throws Exception {
        NodeList nodeList = rootElement.getChildNodes();
        Map<String,String> map = new HashMap<String, String>(nodeList.getLength());
        for(int x=0; x<nodeList.getLength(); x++) {
            Node node = nodeList.item(x);
            if(node.getNodeType() == Node.ELEMENT_NODE) {
                map.put(node.getNodeName(), node.getTextContent());
            }
        }
        return map;
    }

}

人物

您指定字段/属性应通过 @XmlJavaTypeAdapter 注释利用 XmlAdapter.

You specify that a field/property should leverage the XmlAdapter via the @XmlJavaTypeAdapter annotation.

package forum11353790;

import java.io.Serializable;
import java.util.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlAccessorType( XmlAccessType.FIELD )
@XmlRootElement
public class Person implements Serializable {

    private String firstname;

    private String lastname;

    private List< Address > addresses = new ArrayList< Address >();

    @XmlAnyElement
    @XmlJavaTypeAdapter(MapAdapter.class)
    private Map< String, String > data = new HashMap< String, String >();

}

地址

package forum11353790;

import java.io.Serializable;
import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Address implements Serializable {

    private String  street;
    private String  city;
    private String  state;
    private String  country;
    private String  postalcode;
    private Integer addresstype;

}

jaxb.properties

要将 MOXy 指定为您的 JAXB 提供程序,您需要在与域模型相同的包中包含一个名为 jaxb.properties 的文件,其中包含以下条目(请参阅:http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).

To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

演示

下面是一个独立的示例,您可以运行它来证明一切正常.

Below is a standalone example you can run to prove everything works.

package forum11353790;

import java.io.FileInputStream;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;

public class Demo {

    public static void main(String[] args) throws Exception {
        Map<String, Object> properties = new HashMap<String,Object>(2);
        properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
        properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
        JAXBContext jc = JAXBContext.newInstance(new Class[] {Person.class}, properties);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        StreamSource json = new StreamSource(new FileInputStream("src/forum11353790/input.json"));
        Person person = unmarshaller.unmarshal(json, Person.class).getValue();

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(person, System.out);
    }

}

input.json/Output

{
   "firstname" : "Jimmy",
   "lastname" : "Johns",
   "addresses" : [ {
      "street" : "19 Mayberry Drive",
      "city" : "Mayberry",
      "state" : "nc",
      "country" : "us",
      "postalcode" : "27043",
      "addresstype" : 1
   } ],
   "data" : {
      "sandwich" : "roast beef",
      "hair" : "brown",
      "eyes" : "blue"
   }
}

MOXy 和 JAX-RS/Jersey

您可以通过利用 MOXyJsonProvider 类在 JAX-RS 环境中利用 MOXy:

You can leverage MOXy in a JAX-RS environment by leveraging the MOXyJsonProvider class:

  • http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html

相关文章