我正在尝试找到一种方法,将以下 XML / JSON文档正确地映射到等效的 JAXB / Moxy注释类。
请注意,文档的模型元素(在我的示例中描述了一个人)是自由格式,即可能任何类型的XML元素/ JSON对象,这是不为人所知。
XML文档:
<form>
<title>Person Form</title>
<model>
<person>
<name>John</name>
<surname>Smith</surname>
<address>
<street>Main St.</street>
<city>NY</city>
<country>USA</country>
</address>
<person>
</model>
</form>
等效的JSON文档:
{
"title":"Form Title",
"model":{
"person":{
"name":"John",
"surname":"Smith",
"address":{
"street":"Main St.",
"city":"NY",
"country":"USA"
}
}
}
}
我想将模型字段映射为地图,其中值可能是基本类型或自己映射。 这种映射足以满足我的需求。
我尝试使用@XmlReadTransformer,@ XMLWriteTransformer MOXY注释,但没有成功(我在buildAttributeValue中获得的记录参数始终为null)
@XmlRootElement @XmlAccessorType(XmlAccessType.FIELD)
public class Form {
private String title;
private Model model;
....getters and setters....
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Model {
@XmlElement
@XmlReadTransformer(transformerClass = Transformer.class)
@XmlWriteTransformers({ @XmlWriteTransformer(xmlPath = "./*", transformerClass = Transformer.class) })
private Map<String, Object> data;
public Map<String, Object> getData() {
return data;
}
public void setData(Map<String, Object> data) {
this.data = data;
}
public static class Transformer implements AttributeTransformer, FieldTransformer {
private AbstractTransformationMapping tm;
public Transformer() {
}
@Override
public void initialize(AbstractTransformationMapping tm) {
this.tm = tm;
}
@Override
public Map<String, Object> buildAttributeValue(Record r, Object o,
Session s) {
Map<String, Object> data = new HashMap<String, Object>();
// TODO: ????
return data;
}
@Override
public Object buildFieldValue(Object arg0, String arg1, Session arg2) {
// TODO
return null;
}
}
}
您能否建议我解决此问题的正确方法或对“模型”字段建模的不同方式?
答案 0 :(得分:2)
Person(或其他任何类)是否是在运行时可用的类?可以稍微修改XML / JSON以包含指示它对应于哪个类的type属性吗?如果是这样,此处的示例可以帮助您http://blog.bdoughan.com/2012/02/xmlanyelement-and-xmladapter.html
你的模型会有这样的东西
@XmlAnyElement
private List<Parameter> data;
并使用链接示例中的ParameterAdapter,您需要在XML和JSON中包含它正在使用的type属性
{
"title" : "Form Title",
"model" : {
"person" : {
"type" : "test.Person",
"name" : "John",
"surname" : "Smith",
...
}
或
<form>
<title>Person Form</title>
<model>
<person type="test.Person">
<name>John</name>
<surname>Smith</surname>
...
</form>
答案 1 :(得分:2)
如果Model将数据保存为org.w3c.dom.Node,它会对你有用吗?
@XmlAnyElement
public Node data;
然后MOXy将处理这种情况,但我相信如果你读取格式化的XML然后写回JSON(额外换行/空格作为输出中的值)有一个错误。但是,通过此设置,我能够读取/写入您提供的XML并读取/写入您提供的JSON。
解决方案有一点需要注意。目前,JSON解组报告事件的方式是它尝试在属性和元素之间进行区分,如果您在示例中读/写JSON,您会注意到重复的键/值对。我已经为这个问题(https://bugs.eclipse.org/bugs/show_bug.cgi?id=407452)打开了一个错误,但有一个解决方法是设置 unmarshaller上的UnmarshallerProperties.JSON_ATTRIBUTE_PREFIX属性。
如果未设置JSON_ATTRIBUTE_PREFIX,则会将每个键报告为元素和属性,以查看它是否匹配,但如果设置了JSON_ATTRIBUTE_PREFIX,那么只有以该前缀开头的内容才会被视为属性,因此您可以设置JSON_ATTRIBUTE_PREFIX指向您的键名通常不以的开头即:
unmarshaller.setProperty(UnmarshallerProperties.JSON_ATTRIBUTE_PREFIX, "@");
或者要明确
unmarshaller.setProperty(UnmarshallerProperties.JSON_ATTRIBUTE_PREFIX, "THIS_STRING_WILL_NEVER_BE_USED_AS_THE_START_OF_A_KEY_NAME");
在您拥有的示例中,您还需要将JSON_INCLUDE_ROOT属性设置为false,因为JSON中没有根,即:在JSON中没有包装“form”键。
u.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT,false);
答案 2 :(得分:0)
任何JAXB (JSR-222)实现(包括EclipseLink MOXy)都可以作为DOM结构免于信息。您可以使用XmlAdapter
将DOM结构转换为Map或从Map转换。
XmlAdapter(DomMapAdapter)
以下是XmlAdapter
的框架,我实际上并未包含在java.util.Map
和org.w3c.dom.Document
之间进行转换的逻辑。
import java.util.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;
public class DomMapAdapter extends XmlAdapter<Object, Map<String, Object>> {
private Document document;
public DomMapAdapter() {
try {
document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
} catch(Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Map<String, Object> unmarshal(Object v) throws Exception {
Document document = (Document) v;
Map<String, Object> map = new HashMap<String, Object>();
// TODO - Convert Document to Map
return map;
}
@Override
public Object marshal(Map<String, Object> v) throws Exception {
// TODO - Convert Map to Document
return document;
}
}
<强>模型强>
以下是使用XmlAdapter
注释指定XmlJavaTypeAdapter
的方法。
import java.util.Map;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Model {
@XmlJavaTypeAdapter(DomMapAdapter.class)
private Map<String, Object> data;
}
我已输入以下增强请求,这将使此用例更容易。请添加您认为有用的任何信息: