Rest 如何在不改变模型的情况下向Jersey/Moxy/jaxbxml添加结构链接

Rest 如何在不改变模型的情况下向Jersey/Moxy/jaxbxml添加结构链接,rest,jaxb,jersey,moxy,Rest,Jaxb,Jersey,Moxy,我指的是HATEOAS/超媒体API意义上的“结构链接”。更一般的问题是,如何使用依赖于被封送的实体和环境(在本例中,至少是绝对URL)的数据来扩充生成的XML 我使用Jersey 2.9和Moxy 2.5作为JAXB提供者 根据该模型: package testing; import java.util.ArrayList; import java.util.List; public class Planet { private int id = 1; private S

我指的是HATEOAS/超媒体API意义上的“结构链接”。更一般的问题是,如何使用依赖于被封送的实体和环境(在本例中,至少是绝对URL)的数据来扩充生成的XML

我使用Jersey 2.9和Moxy 2.5作为JAXB提供者

根据该模型:

package testing;

import java.util.ArrayList;
import java.util.List;

public class Planet {

    private int id = 1;
    private String name = "test";
    private double radius = 3.0;
    private String href;

    private List<Moon> moons = new ArrayList<Moon>(0);

    public void addMoon(Moon moon) {
        moons.add(moon);
    }
}

...plus Moon class
封装测试;
导入java.util.ArrayList;
导入java.util.List;
公共级行星{
私有int id=1;
私有字符串name=“test”;
私人双半径=3.0;
私有字符串href;
私有列表卫星=新的ArrayList(0);
月亮(月亮){
月亮。添加(月亮);
}
}
…再加上月球班
我想得到类似以下XML(以及等效的JSON)的内容:


测试
3
月亮1
月亮2
模型没有“href”字段,也不能添加。理想情况下,我可以使用UriBuilder直接从资源类获取这些路径

到目前为止,我已经提出了几种可能性。我能要求你考虑哪一个(如果有的话)腿最多,然后你怎么处理这个方法的缺点?< /P> 1.使用AspectJ(或Javassist)扩充模型。 然后使用Jersey中现有的声明性链接机制,所有这些机制都依赖于模型中有一个字段来接收生成的链接。如果在构建过程中没有AspectJ和/或对字节码操纵等外来技术犹豫不决,那么这显然是行不通的

2.对生成的XML和JSON进行后期处理 例如,在MessageBodyWriter中:

ContextResolver<JAXBContext> resolver = providers.getContextResolver(JAXBContext.class, mediaType);
JAXBContext context = resolver.getContext(type);
Marshaller m = context.createMarshaller();
<--- here, marshall to e.g. a DOM then transform that
<--- then manipulate the JSON structures
ContextResolver resolver=providers.getContextResolver(JAXBContext.class,mediaType);
JAXBContext context=resolver.getContext(类型);
Marshaller m=context.createMarshaller();
AspectJ方法
提要
  • 使用AspectJ向模型类添加字段(本例中称为“href”)
  • 将Jersey注释添加到该字段
  • Jersey随后将使用资源类定义的正确URL填充该字段
  • 使用外部映射文件指定href字段的封送处理
您还可以通过相同的AspectJ机制向href添加JAXB注释来指定href的封送

示例代码 这些是信息量最大的部分。有关完整示例,请参见

方面 模型类 不含其他特定积垢的POJO。看

Moxy映射文件 请注意,您可以为任何给定类型选择是否实际封送href字段。事实上,通过使用多个映射文件,您可以在某些表示中包含href字段,而不在其他表示中包含href字段

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="testing"
    xml-mapping-metadata-complete="true"
    xml-accessor-type="NONE">
    <java-types>
        <java-type name="Planet">
            <xml-root-element/>
            <java-attributes>
                <xml-attribute java-attribute="href"/>
                <xml-element java-attribute="name"/>
                <xml-element java-attribute="radius"/>
                <xml-element java-attribute="moons" name="moon">
                    <xml-element-wrapper name="moons"/>
                </xml-element>    
            </java-attributes>
        </java-type>
        <java-type name="Moon">
            <xml-root-element/>
            <java-attributes>
                <xml-attribute java-attribute="href"/>
                <xml-element java-attribute="name"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

样本输出 塔达!从JAX-RS资源类自动派生的结构链接,无需更改模型源代码。因为我们使用的是Moxy,所以我们也可以免费获得JSON

<planet href="http://localhost:8080/reststructlinks/rest/services">
    <name>test</name>
    <radius>3.0</radius>
    <moons>
        <moon href="http://localhost:8080/reststructlinks/rest/services/moons/moon1">
            <name>moon1</name>
        </moon>
        <moon href="http://localhost:8080/reststructlinks/rest/services/moons/moon2">
            <name>moon2</name>
        </moon>
    </moons>
</planet>

测试
3
月亮1
月亮2

这里的AspectJ方法示例:。如果没有其他人,我会加上这个作为回答。re 1:不幸的是,Jersey中的声明性链接功能目前仍处于早期开发阶段。例如,它对(EclipseLink)JPA实体根本不起作用,对属性形成大型对象图(和)的对象的处理速度较慢。我发现的一个潜在的解决方法可能是使用Jersey拦截器机制(),但这种拦截器可能会遇到与您的其他想法相同的问题,即不引用资源URI上下文。我还没有进一步调查。快速搜索使我找到一个线程,该线程建议可以使用@Context UriInfo在拦截器中获取所需信息:。
    <java-type name="Planet">
        <xml-root-element/>            
        <java-attributes>
            <xml-transformation java-attribute="name">
                <xml-write-transformer transformer-class="testing.HrefWriter" xml-path="@href"/>
            </xml-transformation>
            <xml-element java-attribute="name"/>
package testing;

import org.glassfish.jersey.linking.InjectLink;
import org.glassfish.jersey.linking.Binding;

public aspect HrefInjector {

    private String Planet.href;
    declare @field : * Planet.href : @InjectLink(
                                        resource=Services.class, 
                                        style=InjectLink.Style.ABSOLUTE
                                    ) ;

    private String Moon.href;
    declare @field : * Moon.href : @InjectLink(
                                        resource=Services.class,
                                        method="moon",
                                        bindings={@Binding(
                                                name="moonid", value="${instance.name}"
                                                )},
                                        style=InjectLink.Style.ABSOLUTE
                                    ) ;

}
package testing;

import java.util.ArrayList;
import java.util.List;

public class Planet {

    private int id = 1;
    private String name = "test";
    private double radius = 3.0;

    private List<Moon> moons = new ArrayList<Moon>(0);

    public void addMoon(Moon moon) {
        moons.add(moon);
    }
}


package testing;

public class Moon {

    private String name;

    // No-arg constructor is a requirement of JAXB
    public Moon() {
    }

    public Moon(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

}
package testing;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/services")
@Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
public class Services {

    private Planet initPlanet() {
        Planet p = new Planet();
        p.addMoon(new Moon("moon1"));
        p.addMoon(new Moon("moon2"));
        return p;
    }

    @GET
    public Planet planet () {
        return initPlanet();
    }

    @GET @Path("/moons/{moonid}")
    public Moon moon (@PathParam("moonid") String name) {
        return new Moon(name);
    }

}
<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="testing"
    xml-mapping-metadata-complete="true"
    xml-accessor-type="NONE">
    <java-types>
        <java-type name="Planet">
            <xml-root-element/>
            <java-attributes>
                <xml-attribute java-attribute="href"/>
                <xml-element java-attribute="name"/>
                <xml-element java-attribute="radius"/>
                <xml-element java-attribute="moons" name="moon">
                    <xml-element-wrapper name="moons"/>
                </xml-element>    
            </java-attributes>
        </java-type>
        <java-type name="Moon">
            <xml-root-element/>
            <java-attributes>
                <xml-attribute java-attribute="href"/>
                <xml-element java-attribute="name"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>
<planet href="http://localhost:8080/reststructlinks/rest/services">
    <name>test</name>
    <radius>3.0</radius>
    <moons>
        <moon href="http://localhost:8080/reststructlinks/rest/services/moons/moon1">
            <name>moon1</name>
        </moon>
        <moon href="http://localhost:8080/reststructlinks/rest/services/moons/moon2">
            <name>moon2</name>
        </moon>
    </moons>
</planet>