xml:基于JAXB

时间:2012-03-28 11:34:09

标签: java xml jaxb

我有一些像这样的XML:

<root xml:base="http://www.example.com/foo">
  <childElement someAttribute="bar/blort.html"/>
  <childElement someAttribute="bar/baz/foo.html"/>
</root>

我的XML架构将someAttribute定义为类型xs:anyURI

我想使用JAXB将XML解组为对象模型,如下所示:

@XmlRootElement(name="root")
class Root {
    @XmlElement(name="childElement")
    private List<Child> _children;
}

class Child {
    @XmlAttribute(name="someAttribute")
    private URI _someAttribute;
}

我希望someAttribute的值能够根据XML基础解析,即当我解组上面给出的XML时,我希望将孩子们的属性解析为值为http://www.example.com/foo/bar/blort.html的java.net.URI实例,并且等等。

我希望自定义XmlAdapter能够让我获得正确的结果,但是XmlAdapter无法访问周围的上下文,特别是xml:base的值在那时有效(请注意,这不是简单,因为xml:base的最新封闭值为xml:base可以出现在树中的任何位置,并且必须针对它们的祖先解析相对xml:bases。)

如果重要的话,我正在使用EclipseLink的JAXB MOXY实现。

2 个答案:

答案 0 :(得分:1)

您可以利用XMLStreamReaderXmlAdapter来实现此用例:

<强> UriAdapter

UriAdapter既是用于处理XmlAdapter属性的URI,也是用于检测StreamFilter属性的xml:base

package forum9906642;

import java.net.URI;
import javax.xml.bind.annotation.adapters.*;
import javax.xml.stream.*;

public class UriAdapter extends XmlAdapter<String, URI> implements StreamFilter {

    private String base = "";

    public UriAdapter() {
    }

    public UriAdapter(String base) {
        this.base = base;
    }

    public URI unmarshal(String string) throws Exception {
        return new URI(base + '/' + string);
    }

    public String marshal(URI uri) throws Exception {
        if("".equals(base)) {
            return uri.toString();
        } else {
            URI baseURI = new URI(base);
            return baseURI.relativize(uri).toString();
        }
    }

    public boolean accept(XMLStreamReader reader) {
        if(reader.isStartElement()) {
            String newBase = reader.getAttributeValue("http://www.w3.org/XML/1998/namespace", "base");
            if(null != newBase) {
                base = newBase;
            }
        }
        return true;
    }

}

<强>演示

下面的代码演示了如何将所有部分组合在一起:

package forum9906642;

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

public class Demo {

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

        UriAdapter uriAdapter = new UriAdapter();

        XMLInputFactory xif = XMLInputFactory.newFactory();
        XMLStreamReader xsr = xif.createXMLStreamReader(new FileReader("src/forum9906642/input.xml"));
        xsr = xif.createFilteredReader(xsr, uriAdapter);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setAdapter(uriAdapter);
        Root root = (Root) unmarshaller.unmarshal(xsr);

        for(Child child : root.getChildren()) {
            System.out.println(child.getSomeAttribute().toString());
        }

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

}

儿童

package forum9906642;

import java.net.URI;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;

@XmlAccessorType(XmlAccessType.FIELD)
class Child {

    @XmlAttribute(name="someAttribute")
    @XmlJavaTypeAdapter(UriAdapter.class)
    private URI _someAttribute;

    public URI getSomeAttribute() {
        return _someAttribute;
    }

    public void setSomeAttribute(URI _someAttribute) {
        this._someAttribute = _someAttribute;
    }

}

<强>输出

http://www.example.com/foo/bar/blort.html
http://www.example.com/foo/bar/baz/foo.html
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <childElement someAttribute="bar/blort.html"/>
    <childElement someAttribute="bar/baz/foo.html"/>
</root>

答案 1 :(得分:1)

我有类似的问题,但是使用嵌套的xml:base(因为XInclude),所以我最终不得不这样做:

public class URIFixingUnmarshaller {
  private final JAXBContext jaxb;

  public URIFixingUnmarshaller(JAXBContext jaxb) {
    this.jaxb = jaxb;
  }

  public <T> JAXBElement<T> unmarshal(SAXSource in, Class<T> as)
      throws JAXBException {
    CurrLocation curr = new CurrLocation(in.getXMLReader());
    Unmarshaller u = jaxb.createUnmarshaller();
    u.setListener(new URIUpdater(curr));
    return u.unmarshal(new SAXSource(curr, in.getInputSource()), as);
  }

  private static class CurrLocation extends XMLFilterImpl {
    private Locator curr;

    public CurrLocation(XMLReader actual) {
      setParent(actual);
    }

    @Override
    public void setDocumentLocator(Locator to) {
      super.setDocumentLocator(to);
      this.curr = to;
    }

    String resolve(String uri) {
      try {
        URL base = new URL(curr.getSystemId());
        URL absolute = new URL(base, uri);
        return absolute.toString();
      } catch (MalformedURLException probablyAlreadyAbsolute) {
        return uri;
      }
    }
  }

  private static class URIUpdater extends Unmarshaller.Listener {
    private final CurrLocation curr;

    URIUpdater(CurrLocation curr) {
      this.curr = curr;
    }

    @Override
    public void afterUnmarshal(Object target, Object parent) {
      if (target instanceof SomethingWithRelativeURI) {
        SomethingWithRelativeURI casted = (SomethingWithRelativeURI) target;

        casted.setPath(curr.resolve(casted.getPath()));
      }
    }
  }
}