3

我正在尝试找到一种方法将以下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"  
         }          
      }  
   }
}

我想将模型字段映射为Map,其中的可能是原始类型Map 本身。这种映射足以满足我的需求。

我尝试使用@XmlReadTransformer、@XmlWriteTransformer MOXY 注释,但没有成功(我在 buildAttributeValue 中得到的记录参数始终为空)

@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;
        }

    }
}

您能否建议我解决此问题的正确方法或建模“模型”字段的不同方法?

4

3 回答 3

2

Person(或您拥有的任何其他类)是在运行时可用的类吗?是否可以稍微修改 XML/JSON 以包含指示它对应于哪个类的类型属性?如果是这样,这里的示例可能会对您有所帮助 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>
于 2013-05-02T20:02:11.220 回答
2

如果 Model 将数据保存为 org.w3c.dom.Node 那么它对你有用吗?

 @XmlAnyElement
 public Node data;

然后 MOXy 将处理这种情况,但我相信如果您读取格式化的 XML 然后写回 JSON(额外的换行符/空格作为输出中的值),则会出现错误。但是,通过此设置,我能够读取/写入您提供的 XML 并读取/写入您提供的 JSON。

解决方案有一点需要注意。目前,JSON unmarshal 报告事件的方式试图区分属性和元素,如果您在示例中读/写 JSON,您会注意到重复的键/值对。我已经为这个问题打开了一个错误(https://bugs.eclipse.org/bugs/show_bug.cgi?id=407452)但是有一个解决方法是在解组器上设置 UnmarshallerProperties.JSON_ATTRIBUTE_PREFIX 属性。

如果该 JSON_ATTRIBUTE_PREFIX 未设置,它会将每个键报告为一个元素和一个属性以查看它是否匹配,但如果您设置 JSON_ATTRIBUTE_PREFIX 则只有以该前缀开头的内容将被视为属性,因此您可以将 JSON_ATTRIBUTE_PREFIX 设置为您的键名通常不以 ie 开头的任何内容:

   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 中没有包装“表单”键。

   u.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT,false);
于 2013-05-07T18:23:07.657 回答
0

任何JAXB (JSR-222)实现(包括EclipseLink MOXy)都可以作为 DOM 结构保持信息的自由。您可以使用XmlAdapter将该 DOM 结构转换为 Map 或从 Map 转换。

XmlAdapter (DomMapAdapter)

下面是 的骨架XmlAdapter,我实际上并没有包含在 ajava.util.Maporg.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;

}

我已经输入了以下增强请求,这将使这个用例更容易。请添加您认为有帮助的任何信息:

于 2013-05-03T18:32:53.637 回答