我有一些像这样的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实现。
答案 0 :(得分:1)
您可以利用XMLStreamReader
和XmlAdapter
来实现此用例:
<强> 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()));
}
}
}
}