2013-12-23 36 views
6

我有一些使用JAX-RS和Jackson的REST服務。我的客戶希望抽象類作爲服務的返回類型,我不知道如何讓JAX-RS客戶端返回具體子類的實例。這是可能的XML和JSON表示?如果是這樣,將欣賞樣本和/或鏈接。JAX-RS:從聲明抽象返回類型的方法返回具體類實例

+0

也許@XmlSeeAlso使用JAXB? – nachokk

+0

我寫了一個測試程序。 @XmlSeeAlso工作。 JSON怎麼樣? –

+1

如果您在教程中使用球衣,說jaxb適用於json和xml .. – nachokk

回答

0

有幾種方法可以實現這一點。最簡單的方法是使用@JsonTypeInfo,as Garry mentioned。你所要做的就是用@JsonTypeInfo(use = Id.CLASS, include = As.PROPERTY)註釋你的抽象基類,你應該很好走。

另一種方法是確保服務器端和客戶端上的所有映射器都啓用(可能爲NON_FINAL)default typing,以便在未註釋的類上對(類型)信息進行序列化。如果您對開箱即用的序列化功能不滿意,可以通過提供自己的TypeResolverBuilder來加強這兩種方式。這些方法的更詳細的解釋可以在in this articleJackson's Polymorphic Type Handling wiki page找到。

儘管@JsonTypeInfo的方式很簡單,但實際上獲得CXF服務器/客戶端可能是一件苦差事。所以這裏有一個完整的例子:

import com.fasterxml.jackson.annotation.JsonTypeInfo; 
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; 
import com.fasterxml.jackson.jaxrs.xml.JacksonJaxbXMLProvider; 
import org.apache.cxf.endpoint.Server; 
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; 
import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean; 

import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
import javax.ws.rs.Produces; 
import java.util.Arrays; 
import java.util.Collections; 
import java.util.Random; 

import static javax.ws.rs.core.MediaType.APPLICATION_JSON; 
import static javax.ws.rs.core.MediaType.APPLICATION_XML; 

public class FullCxfJaxrsJacksonExample { 
    public static void main(String[] args) { 
     String serverAddress = "http://localhost:9000/"; 
     Server server = null; 

     try { 
      // make server that supports JSON and XML 
      JAXRSServerFactoryBean serverFactory = new JAXRSServerFactoryBean(); 
      serverFactory.setResourceClasses(ShapeServiceRandom.class); 
      serverFactory.setAddress(serverAddress); 
      serverFactory.setProviders(Arrays.asList(new JacksonJaxbJsonProvider(), new JacksonJaxbXMLProvider())); 
      server = serverFactory.create(); 

      // make and use a client 
      JAXRSClientFactoryBean clientFactory = new JAXRSClientFactoryBean(); 
      clientFactory.setAddress(serverAddress); 
      clientFactory.setServiceClass(ShapeService.class); 
      clientFactory.setProvider(new JacksonJaxbJsonProvider()); 
      clientFactory.setHeaders(Collections.singletonMap("Accept", "application/json")); 
      // for an XML client instead of a JSON client, use the following provider/headers instead: 
      //clientFactory.setProvider(new JacksonJaxbXMLProvider()); 
      //clientFactory.setHeaders(Collections.singletonMap("Accept", "application/xml")); 

      ShapeService shapeServiceClient = clientFactory.create(ShapeService.class); 
      for (int i = 0; i < 10; i++) { 
       System.out.format("created shape: %s\n", shapeServiceClient.create()); 
      } 

     } finally { 
      if (server != null) { 
       server.destroy(); 
      } 
     } 
     System.exit(0); 
    } 

    // Put JsonTypeInfo on the abstract base class so class info gets marshalled. 
    // You'll want CLASS instead of MINIMAL_CLASS if your base class and subclasses are in different packages. 
    @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY) 
    public abstract static class Shape { 
    } 

    public static class Circle extends Shape { 
     public double radius; 

     @Override 
     public String toString() { 
      return "Circle{radius=" + radius + '}'; 
     } 
    } 

    public static class Polygon extends Shape { 
     public int numSides; 

     @Override 
     public String toString() { 
      return "Polygon{numSides=" + numSides + '}'; 
     } 
    } 

    // service definition with abstract return type 
    @Path("/shape") 
    public interface ShapeService { 
     @GET 
     @Path("/create") 
     @Produces({APPLICATION_JSON, APPLICATION_XML}) 
     Shape create(); 
    } 

    // service implementation that returns different concrete subclasses 
    public static class ShapeServiceRandom implements ShapeService { 
     Random random = new Random(); 

     public Shape create() { 
      int num = random.nextInt(8); 
      if (num > 3) { 
       Polygon polygon = new Polygon(); 
       polygon.numSides = num; 
       return polygon; 
      } else { 
       Circle circle = new Circle(); 
       circle.radius = num + 0.5; 
       return circle; 
      } 
     } 
    } 
} 

這個例子使用的是JDK 1.8.0_45成功進行了測試和下面的依賴關係:

<dependency> 
     <groupId>com.fasterxml.jackson.jaxrs</groupId> 
     <artifactId>jackson-jaxrs-json-provider</artifactId> 
     <version>2.5.4</version> 
    </dependency> 
    <dependency> 
     <groupId>com.fasterxml.jackson.jaxrs</groupId> 
     <artifactId>jackson-jaxrs-xml-provider</artifactId> 
     <version>2.5.4</version> 
    </dependency> 
    <dependency> 
     <groupId>org.apache.cxf</groupId> 
     <artifactId>cxf-rt-transports-http-jetty</artifactId> 
     <version>3.1.1</version> 
    </dependency> 
    <dependency> 
     <groupId>org.apache.cxf</groupId> 
     <artifactId>cxf-rt-rs-client</artifactId> 
     <version>3.1.1</version> 
    </dependency> 
+1

謝謝你的時間 –

+0

很高興看到完整的代碼:) – Garry

2

你可以嘗試添加JsonTypeInfo和JsonSubTypes註釋

@JsonTypeInfo(use = Id.CLASS, 
       include = JsonTypeInfo.As.PROPERTY, 
       property = "type") 
@JsonSubTypes({ 
    @Type(value = MySubClass.class) 
    }) 
public abstract class MyAbsClass { 
.... 
} 
public class MySubClass extends MyAbsClass { 
.... 
} 

它應該增加的類型信息,以JSON輸出。

0

如果您只想返回一個任意具體實例,那麼在使用基於Jackson的提供程序時,這個「正常工作」。這是JSON的默認值DropWizard。可能發生的唯一問題是使用plain Jersey和基於JAXB的提供程序,這可能會限制序列化爲抽象類型公開的API。

如果您還需要能夠將客戶端反序列化爲具體類型,則需要使用@JsonTypeInfo

相關問題