2012-03-28 63 views
1

我有一些这样的XML:XML:基地JAXB

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

我的XML架构someAttribute定义为类型为xs:任何URI

我想使用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,我希望儿童的属性加以解决吨o值为http://www.example.com/foo/bar/blort.html的java.net.URI实例等。

我希望自定义的XmlAdapter可以让我获得正确的结果,但是XmlAdapter无法访问周围的上下文,特别是xml:base在这个时候的值(注意这不是就像xml:base的最近封闭值一样简单,因为xml:base可以出现在树中的任何位置,而相对的xml:bases必须根据它们的祖先解析)。

我使用EclipseLink的JAXB的MOXY实现,如果它很重要。

回答

1

您可以利用的XMLStreamReaderXmlAdapter实现这个用例:

UriAdapter

UriAdapter既是XmlAdapter用于处理URI财产和StreamFilter,我们将用它来检测​​属性。下面

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

谢谢!它需要稍微调整以处理由祖先元素上的多个xml:base属性确定的基本URL的边缘情况,但这可以使用reader.isEndElement并维护在树中的每个深度处找到的xml:base列表来完成 – 2012-03-28 15:39:10

1

我也有类似的问题,但与嵌套的xml:(因为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())); 
     } 
    } 
    } 
}