【问题标题】:XML mapping attribute of nested element嵌套元素的 XML 映射属性
【发布时间】:2012-09-07 16:05:53
【问题描述】:

我正在使用 XStream,并且我有一个 XML 示例:

<person>
    <firstname>Joe</firstname>
    <lastname>Walnes</lastname>
    <phone value="1234-456" />
    <fax value="9999-999" />
</person>

我想把它映射到类

public class Person {

    private String firstname;
    private String lastname;
    private String phone;
    private String fax;

}

所以想法是将嵌套元素的属性映射到当前对象。 我试图找到任何现成的转换器,但没有成功。我相信通过实施新的转换器可以做到这一点,但可能有人已经这样做了。或者有一个我还没有找到的解决方案。

更新:

我试图实现的想法是省略不必要的创建和映射实体。我根本不需要电话和传真实体,我只需要模型中的属性。我尝试解析的 XML 架构对我来说是第三方的,我无法更改它。

【问题讨论】:

  • 好发现!我已经解决了这个问题。只是从头开始写的。
  • 你能再澄清一下你在寻找什么吗?你说“我不需要电话和传真实体......”,你没有它们 - 它们只是模型中的字符串,而不是单独的实体......你只想要前两个属性映射还是四个?
  • 问题是如何将 XML 样本映射到模型。无需任何额外努力,XStream 就假定 XML 元素无论如何都被解释为模型成员(即使它们被显式省略)。拥有丰富的 XML 模式,您可能希望简化模型以逃避有意义数据的冗余“持有者”。隐式集合很好,但还不够。

标签: java xml parsing xstream


【解决方案1】:

请参阅 XStream 别名教程的“属性别名”部分:http://x-stream.github.io/alias-tutorial.html

【讨论】:

  • 谢谢奥拉夫。我已经更新了这个问题。我想知道它可以通过教程中的场景来完成吗?
【解决方案2】:

我不知道有什么现成的转换器可以做到这一点,但是写一个很简单

import com.thoughtworks.xstream.converters.*;
import com.thoughtworks.xstream.io.*;

public class ValueAttributeConverter implements Converter {
  public boolean canConvert(Class cls) {
    return (cls == String.class);
  }

  public void marshal(Object source, HierarchicalStreamWriter w, MarshallingContext ctx) {
    w.addAttribute("value", (String)source);
  }

  public Object unmarshal(HierarchicalStreamReader r, UnmarshallingContext ctx) {
    return r.getAttribute("value");
  }
}

您可以使用注释将转换器附加到相关字段

import com.thoughtworks.xstream.annotations.*;

@XStreamAlias("person")
public class Person {

    private String firstname;
    private String lastname;

    @XStreamConverter(ValueAttributeConverter.class)
    private String phone;

    @XStreamConverter(ValueAttributeConverter.class)
    private String fax;

    // add appropriate constructor(s)

    /** For testing purposes - not required by XStream itself */
    public String toString() {
      return "fn: " + firstname + ", ln: " + lastname +
             ", p: " + phone + ", f: " + fax;
    }
}

要完成这项工作,您需要做的就是指示 XStream 读取注释:

XStream xs = new XStream();
xs.processAnnotations(Person.class);
Person p = (Person)xs.fromXML(
  "<person>\n" +
  "  <firstname>Joe</firstname>\n" +
  "  <lastname>Walnes</lastname>\n" +
  "  <phone value='1234-456' />\n" +
  "  <fax value='9999-999' />\n" +
  "</person>");
System.out.println(p);
// prints fn: Joe, ln: Walnes, p: 1234-456, f: 9999-999

【讨论】:

  • 谢谢,我去看看。不确定我是否了解它在那里的工作原理。可能是因为根本不了解转换器。
  • 当 XStream 正在读取表示 b​​ean 类型类的 XML 时,它会读取元素名称以确定它正在处理的字段,然后委托给相关的转换器。转换器的unmarshal 方法传递了一个HierarchicalStreamReader,它指向(在这种情况下)phone 元素的开始标记。普通的单值转换器对元素的文本内容进行操作,但我的自定义转换器改为查看名为 value 的属性 - 它不需要关心元素名称是什么。
【解决方案3】:

注意:我是EclipseLink JAXB (MOXy) 领导,也是JAXB (JSR-222) 专家组的成员。

如果您愿意使用 XStream 以外的库,以下是在 EclipseLink JAXB (MOXy) 中利用 @XmlPath 扩展的方法。

人物

@XmlPath 注释允许您通过 XPath 将您的字段/属性映射到 XML 文档中的某个位置(请参阅:http://blog.bdoughan.com/2010/07/xpath-based-mapping.html)。

package forum12425401;

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Person {

    private String firstname;
    private String lastname;

    @XmlPath("phone/@value")
    private String phone;

    @XmlPath("fax/@value")
    private String fax;

}

jaxb.properties

要将 MOXy 指定为您的 JAXB 提供程序,您需要在与域模型相同的包中包含一个名为 jaxb.properties 的文件,并使用以下条目(请参阅:http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

演示

下面的代码会将 XML 转换为您的领域模型,然后将领域模型写回 XML。

package forum12425401;

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Person.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum12425401/input.xml");
        Person person = (Person) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(person, System.out);
    }

}

input.xml/Output

<?xml version="1.0" encoding="UTF-8"?>
<person>
   <firstname>Joe</firstname>
   <lastname>Walnes</lastname>
   <phone value="1234-456"/>
   <fax value="9999-999"/>
</person>

【讨论】:

  • 非常感谢您提供如此详细的答案。我发现 JAXB 正在调查这个问题,而 @XmlPath 是我关心的第一件事。您能否简要解释一下现在它的工作原理,我的意思是它是否需要拉出任何额外的元素?性能和数据效率是我目前最强烈的要求。
  • @ViktorStolbin - @XmlPath 实际上是在 JAXB (JSR-222) 规范的 EclipseLink MOXy 实现中提供的扩展。 MOXy 支持 XPath 的子集,可以在 XML 文档的单次深度优先遍历期间进行处理。这是一个非常有效的过程。
猜你喜欢
  • 1970-01-01
  • 2020-11-16
  • 2019-01-02
  • 2013-03-01
  • 2015-11-07
  • 2011-01-11
  • 1970-01-01
  • 1970-01-01
  • 2015-03-07
相关资源
最近更新 更多