2012-03-28 70 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())); 
     } 
    } 
    } 
}