JAXB用于为JSON或XML自然返回的列表

时间:2014-01-02 19:54:25

标签: java xml json moxy

我使用MOXy和Jersey实现RESTful API,并希望自然地为JSON和XML返回列表,我的意思是XML包含整个集合的元素标记以及集合项,而JSON只包含集合的标记。

例如,我想要返回一个"组织"具有嵌套的设施和位置列表的资源。作为XML:

<organisation>
  <id>1</id>
  <name>XYZ</name>
  <facilities>
    <facility>
      <id>1</id>
      <text>Telephone</text>
    </facility>
    <facility>
      <id>3</id>
      <text>Whiteboard</text>
    </facility>
  </facilities>
  <locations>
    <location>
      <id>1</id>
      <kind>REGION</kind>
      <name>London</name>
    </location>
    <location>
      <id>2</id>
      <kind>REGION</kind>
      <name>Manchester</name>
    </location>
  </locations>
</organisation>

和JSON一样:

{
  "id": 1,
  "name": "XYZ",
  "facilities": [
    {
      "id": 1,
      "text": "Telephone"
    },
    {
      "id": 3,
      "text": "Whiteboard"
    }
  ],
  "locations": [
    {
      "id": 1,
      "kind": "REGION",
      "name": "London"
    },
    {
      "id": 2,
      "kind": "REGION",
      "name": "Manchester"
    }
  ]
}

不幸的是,我似乎无法获得一个代码库,允许我为XML和JSON返回这样的输出。如果我使用一个类来包装嵌套列表,那么XML看起来是正确的而不是JSON(参见&#34; ExternalFacilities&#34;下面)。如果我将嵌套列表定义为ArrayList子类,则JSON显示正确但不是XML(请参阅&#34; ExternalLocations&#34;下面)。

XML示例显示&#34;设施&#34;列出正确但不是&#34;位置&#34;

看到没有包含&#34;位置&#34;的XML元素。 list(就像&#34;设施&#34;),以及每个位置都有一个复数元素名称。

<organisation>
  <id>1</id>
  <name>XYZ</name>
  <facilities>
    <facility>
      <id>1</id>
      <text>Telephone</text>
    </facility>
    <facility>
        <id>3</id>
        <text>Whiteboard</text>
    </facility>
  </facilities>
  <locations>
    <id>1</id>
    <kind>REGION</kind>
    <name>London</name>
  </locations>
  <locations>
    <id>2</id>
    <kind>REGION</kind>
    <name>Manchester</name>
  </locations>
</organisation>

JSON示例显示&#34;位置&#34;列出正确但不是&#34;设施&#34;

看到&#34;设施&#34; list是一个包含JSON数组的JSON对象,而我只想要JSON数组(带有复数元素名称)。

{
  "id": 1,
  "name": "XYZ",
  "facilities": {
    "facility": [
        {
            "id": 1,
            "text": "Telephone"
        },
        {
            "id": 3,
            "text": "Whiteboard"
        }
    ]
  },
  "locations": [
    {
      "id": 1,
      "kind": "REGION",
      "name": "London"
    },
    {
      "id": 2,
      "kind": "REGION",
      "name": "Manchester"
    }
  ]
}

以上示例使用相同的代码生成,只需更改Accept HTTP标头即可使Jersey返回JSON而不是XML。以下是课程的摘录:

ExternalOrganisation.java

@XmlRootElement(name="organisation")
@XmlAccessorType(XmlAccessType.FIELD)
public class ExternalOrganisation {

   private String             name;
   private ExternalFacilities facilities;
   private ExternalLocations  locations;

   ...

}

ExternalFacilities.java

@XmlRootElement(name="facilities")
public class ExternalFacilities {
  @XmlElementRef
  private List<ExternalFacility> list;
  ...
}

ExternalLocations.java

@XmlRootElement(name="locations")
public class ExternalLocations extends ArrayList<ExternalLocation> {
  ...
}

ExternalFacility.java

@XmlRootElement(name="facility")
@XmlType(propOrder={"id", "uri", "kind", "text"})
public class ExternalFacility extends ExternalBase {
    // id inherited
    private String text;
    ....
}

ExternalLocation.java

@XmlRootElement(name="location")
@XmlType(propOrder={"id", "kind", "name"})
public class ExternalLocation extends ExternalBase {
    // id inherited
    @XmlElement private LocationKind kind;
    @XmlElement private String       name;
    ...
}

最初似乎与this question类似,但我并没有尝试在我的列表中混合使用对象类型。

1 个答案:

答案 0 :(得分:4)

您可以执行以下操作以获取所需的XML和JSON表示:

第1步 - 利用@XMLElementWrapper

而不是:

@XmlRootElement(name="organisation")
@XmlAccessorType(XmlAccessType.FIELD)
public class ExternalOrganisation {

   private String             name;
   private ExternalFacilities facilities;
   private ExternalLocations  locations;

   ...

}

您可以使用@XmlElementWrapper执行以下操作(请参阅:http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html):

@XmlRootElement(name="organisation")
@XmlAccessorType(XmlAccessType.FIELD)
public class ExternalOrganisation {

   private String             name;

   @XmlElementWrapper
   @XmlElementRef
   private List<ExternalFacility> facilities;

   @XmlElementWrapper
   @XmlElementRef
   private List<ExternalLocation>  locations;

   ...

}

步骤#2 - 利用MOXy的包装器作为阵列名称属性

通过将包装器指定为数组名称属性,MOXy将使用@XmlElementWrapper中的值作为JSON数组名称。

import java.util.*;
import javax.ws.rs.core.Application;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;

public class YourApplication  extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        HashSet<Class<?>> set = new HashSet<Class<?>>(1);
        set.add(YourService.class);
        return set;
    }

    @Override
    public Set<Object> getSingletons() {
        MOXyJsonProvider moxyJsonProvider = new MOXyJsonProvider();
        moxyJsonProvider.setWrapperAsArrayName(true);

        HashSet<Object> set = new HashSet<Object>(1);
        set.add(moxyJsonProvider);
        return set;
    }

} 

了解更多信息