JAXB: как игнорировать пространство имен во время unmarshalling XML-документа?
мои схемы задает пространство имен, но документов нет. Какой самый простой способ игнорировать пространства имен во время распаковки JAXB (объект XML ->)?
другими словами, у меня есть
<foo><bar></bar></foo>
вместо
<foo xmlns="http://tempuri.org/"><bar></bar></foo>
5 ответов:
Я считаю, что вы должны добавить пространство имен для вашего xml-документа, например, с использованием SAX filter.
Это значит:
- определите интерфейс ContentHandler с новым классом, который будет перехватывать события SAX до того, как JAXB сможет их получить.
- определите XMLReader, который установит обработчик содержимого
затем свяжите их вместе:
public static Object unmarshallWithFilter(Unmarshaller unmarshaller, java.io.File source) throws FileNotFoundException, JAXBException { FileReader fr = null; try { fr = new FileReader(source); XMLReader reader = new NamespaceFilterXMLReader(); InputSource is = new InputSource(fr); SAXSource ss = new SAXSource(reader, is); return unmarshaller.unmarshal(ss); } catch (SAXException e) { //not technically a jaxb exception, but close enough throw new JAXBException(e); } catch (ParserConfigurationException e) { //not technically a jaxb exception, but close enough throw new JAXBException(e); } finally { FileUtil.close(fr); //replace with this some safe close method you have } }
вот расширение / редактирование решения VonCs на всякий случай, если кто-то не хочет проходить через хлопоты по реализации своего собственного фильтра для этого. Он также показывает, как вывести элемент JAXB без присутствия пространства имен. Все это выполняется с помощью фильтра SAX.
реализация фильтра:
import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.XMLFilterImpl; public class NamespaceFilter extends XMLFilterImpl { private String usedNamespaceUri; private boolean addNamespace; //State variable private boolean addedNamespace = false; public NamespaceFilter(String namespaceUri, boolean addNamespace) { super(); if (addNamespace) this.usedNamespaceUri = namespaceUri; else this.usedNamespaceUri = ""; this.addNamespace = addNamespace; } @Override public void startDocument() throws SAXException { super.startDocument(); if (addNamespace) { startControlledPrefixMapping(); } } @Override public void startElement(String arg0, String arg1, String arg2, Attributes arg3) throws SAXException { super.startElement(this.usedNamespaceUri, arg1, arg2, arg3); } @Override public void endElement(String arg0, String arg1, String arg2) throws SAXException { super.endElement(this.usedNamespaceUri, arg1, arg2); } @Override public void startPrefixMapping(String prefix, String url) throws SAXException { if (addNamespace) { this.startControlledPrefixMapping(); } else { //Remove the namespace, i.e. don´t call startPrefixMapping for parent! } } private void startControlledPrefixMapping() throws SAXException { if (this.addNamespace && !this.addedNamespace) { //We should add namespace since it is set and has not yet been done. super.startPrefixMapping("", this.usedNamespaceUri); //Make sure we dont do it twice this.addedNamespace = true; } } }
этот фильтр предназначен для добавления пространства имен, если его нет:
new NamespaceFilter("http://www.example.com/namespaceurl", true);
и удалить любой подарок пространство имен:
new NamespaceFilter(null, false);
фильтр можно использовать при разборе следующим образом:
//Prepare JAXB objects JAXBContext jc = JAXBContext.newInstance("jaxb.package"); Unmarshaller u = jc.createUnmarshaller(); //Create an XMLReader to use with our filter XMLReader reader = XMLReaderFactory.createXMLReader(); //Create the filter (to add namespace) and set the xmlReader as its parent. NamespaceFilter inFilter = new NamespaceFilter("http://www.example.com/namespaceurl", true); inFilter.setParent(reader); //Prepare the input, in this case a java.io.File (output) InputSource is = new InputSource(new FileInputStream(output)); //Create a SAXSource specifying the filter SAXSource source = new SAXSource(inFilter, is); //Do unmarshalling Object myJaxbObject = u.unmarshal(source);
чтобы использовать этот фильтр для вывода XML из объекта JAXB, посмотрите на код ниже.
//Prepare JAXB objects JAXBContext jc = JAXBContext.newInstance("jaxb.package"); Marshaller m = jc.createMarshaller(); //Define an output file File output = new File("test.xml"); //Create a filter that will remove the xmlns attribute NamespaceFilter outFilter = new NamespaceFilter(null, false); //Do some formatting, this is obviously optional and may effect performance OutputFormat format = new OutputFormat(); format.setIndent(true); format.setNewlines(true); //Create a new org.dom4j.io.XMLWriter that will serve as the //ContentHandler for our filter. XMLWriter writer = new XMLWriter(new FileOutputStream(output), format); //Attach the writer to the filter outFilter.setContentHandler(writer); //Tell JAXB to marshall to the filter which in turn will call the writer m.marshal(myJaxbObject, outFilter);
это, надеюсь, поможет кому-то, так как я провел день, делая это, и почти сдался дважды ;)
У меня проблемы с кодировкой с решением XMLFilter, поэтому я сделал XMLStreamReader, чтобы игнорировать пространства имен:
class XMLReaderWithoutNamespace extends StreamReaderDelegate { public XMLReaderWithoutNamespace(XMLStreamReader reader) { super(reader); } @Override public String getAttributeNamespace(int arg0) { return ""; } @Override public String getNamespaceURI() { return ""; } } InputStream is = new FileInputStream(name); XMLStreamReader xsr = XMLInputFactory.newFactory().createXMLStreamReader(is); XMLReaderWithoutNamespace xr = new XMLReaderWithoutNamespace(xsr); Unmarshaller um = jc.createUnmarshaller(); Object res = um.unmarshal(xr);
в моей ситуации у меня много пространств имен, и после некоторой отладки я нахожу другое решение, просто меняя класс NamespaceFitler. Для моей ситуации (просто unmarshall) эта работа прекрасна.
import javax.xml.namespace.QName; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.XMLFilterImpl; import com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector; public class NamespaceFilter extends XMLFilterImpl { private SAXConnector saxConnector; @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { if(saxConnector != null) { Collection<QName> expected = saxConnector.getContext().getCurrentExpectedElements(); for(QName expectedQname : expected) { if(localName.equals(expectedQname.getLocalPart())) { super.startElement(expectedQname.getNamespaceURI(), localName, qName, atts); return; } } } super.startElement(uri, localName, qName, atts); } @Override public void setContentHandler(ContentHandler handler) { super.setContentHandler(handler); if(handler instanceof SAXConnector) { saxConnector = (SAXConnector) handler; } } }
другой способ добавить пространство имен по умолчанию в XML-документ перед его подачей в JAXB-использовать JDom:
- разбор XML в документ
- повторите и установите пространство имен для всех элементов
- Unmarshall с помощью JDOMSource
такой:
public class XMLObjectFactory { private static Namespace DEFAULT_NS = Namespace.getNamespace("http://tempuri.org/"); public static Object createObject(InputStream in) { try { SAXBuilder sb = new SAXBuilder(false); Document doc = sb.build(in); setNamespace(doc.getRootElement(), DEFAULT_NS, true); Source src = new JDOMSource(doc); JAXBContext context = JAXBContext.newInstance("org.tempuri"); Unmarshaller unmarshaller = context.createUnmarshaller(); JAXBElement root = unmarshaller.unmarshal(src); return root.getValue(); } catch (Exception e) { throw new RuntimeException("Failed to create Object", e); } } private static void setNamespace(Element elem, Namespace ns, boolean recurse) { elem.setNamespace(ns); if (recurse) { for (Object o : elem.getChildren()) { setNamespace((Element) o, ns, recurse); } } }