我有一个使用JAX-RS和JAXB的典型Web服务,在解组时我想知道JAXB明确调用了哪些setter.这有效地让我知道调用者提供的文档中包含哪些元素.
我知道我可以使用XmlAdapter解决这个问题,但是我在很多不同的包中都有很多类,我不想为每一个创建适配器.我也不想把钩子放进每一个二传手.如果可能,我想要一个通用的解决方案.请注意,我的所有类都设置为使用getter和setter;它们都不使用访问类型的字段.
我的服务使用Jersey 2.4,Spring 3.2和MOXy 2.5.1,所以如果有任何可以利用其中任何一个的东西,那就更好了.我们最初的想法是我们可以动态创建一个工厂类(类似于@XmlType支持的),它将返回一个拦截setter的代理对象.我们认为我们可以使用MOXy中的MetadataSource概念来实现这一点,但这似乎不可能.
有人有主意吗?
最佳答案
My service uses Jersey 2.4, Spring 3.2, and MOXy 2.5.1, so if there’s
anything that can be leveraged from any of those, that’s all the
better.
创建自己的EclipseLink AttributeAccessor
MOXy(EclipseLink的一个组件)利用名为AttributeAccessor的类对字段和属性进行操作.您可以包装此类以捕获所需的所有信息.
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.mappings.AttributeAccessor;
public class MyAttributeAccessor extends AttributeAccessor {
private AttributeAccessor attributeAccessor;
public MyAttributeAccessor(AttributeAccessor attributeAccessor) {
this.attributeAccessor = attributeAccessor;
}
@Override
public Object getAttributeValueFromObject(Object domainObject)
throws DescriptorException {
return attributeAccessor.getAttributeValueFromObject(domainObject);
}
@Override
public void setAttributeValueInObject(Object domainObject, Object value)
throws DescriptorException {
System.out.println("Thread: " + Thread.currentThread().getId() + " - Set value: " + value + " on property: " + attributeAccessor.getAttributeName() + " for object: " + domainObject);
attributeAccessor.setAttributeValueInObject(domainObject, value);
}
}
告诉MOXy使用您的AttributeAccessor
我们可以利用SessionEventListener来访问底层元数据,以指定AttributeAccessor的实现.在创建JAXBContext时,它将作为属性传入.
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextProperties.SESSION_EVENT_LISTENER, new SessionEventAdapter() {
@Override
public void postLogin(SessionEvent event) {
Project project = event.getSession().getProject();
for(ClassDescriptor descriptor : project.getOrderedDescriptors()) {
for(DatabaseMapping mapping : descriptor.getMappings()) {
mapping.setAttributeAccessor(new MyAttributeAccessor(mapping.getAttributeAccessor()));
}
}
super.preLogin(event);
}
});
JAXBContext jc = JAXBContext.newInstance(new Class[] {Foo.class}, properties);
在创建JAXBContext时利用JAX-RS ContextResolver
由于您处于JAX-RS环境中,因此可以利用ContextResolver控制JAXBContext的创建方式.
> http://blog.bdoughan.com/2011/04/moxys-xml-metadata-in-jax-rs-service.html
独立示例
Java模型(Foo)
下面是一个示例类,我们将使用字段访问(没有setter).
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
private String bar;
private String baz;
}
演示
import java.io.StringReader;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.sessions.*;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextProperties.SESSION_EVENT_LISTENER, new SessionEventAdapter() {
@Override
public void postLogin(SessionEvent event) {
Project project = event.getSession().getProject();
for(ClassDescriptor descriptor : project.getOrderedDescriptors()) {
for(DatabaseMapping mapping : descriptor.getMappings()) {
mapping.setAttributeAccessor(new MyAttributeAccessor(mapping.getAttributeAccessor()));
}
}
super.preLogin(event);
}
});
JAXBContext jc = JAXBContext.newInstance(new Class[] {Foo.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StringReader xml = new StringReader("<foo><bar>Hello World</bar></foo>");
Foo foo = (Foo) unmarshaller.unmarshal(xml);
}
}
产量
Thread: 1 - Set value: Hello World on property: bar for object: forum21044956.Foo@37e47e38
UPDATE
So this works, but I have a few issues. First, the domainObject is
always logging as 0 in my system. Not sure why that’s occurring.
我不知道为什么会发生这种情况,可能需要检查要记录的对象的toString().
Second, I am not able to tell if the property in question is on the
top-level item that is being unmarshalled or on a sub-element. That’s
actually quite annoying.
你需要在这里加强逻辑.根据设置的对象,您应该能够做您想做的事情.
Third, your solution is per JAXBContext, but I don’t know if I really
want to create a new context for every request. Isn’t that bad from an
overhead perspective?
您可以缓存创建的JAXBContext以防止重建它.