我的架构指定了一个名称空间,但是文档没有。在JAXB解组(XML-> object)期间忽略名称空间的最简单方法是什么?
换句话说,我有
<foo><bar></bar></foo>
代替,
<foo xmlns="http://tempuri.org/"><bar></bar></foo>
Answers:
我相信您必须将名称空间添加到xml文档中,例如,使用SAX过滤器。
这意味着:
然后将两者链接在一起:
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);
要使用此过滤器从JAXB对象输出XML,请看下面的代码。
//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);
FileInputStream
:)
在我的情况下,我有很多命名空间,经过一些调试后,我发现了另一个解决方案,只是更改了NamespaceFitler类。对于我的情况(只是解组),这个工作很好。
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:
像这样:
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);
}
}
}
如果要在解析过程中将一个名称空间替换为另一个名称空间,这只是对lunicon答案的修改(https://stackoverflow.com/a/24387115/3519572)。而且,如果您想查看到底发生了什么,只需取消注释输出行并设置一个断点即可。
public class XMLReaderWithNamespaceCorrection extends StreamReaderDelegate {
private final String wrongNamespace;
private final String correctNamespace;
public XMLReaderWithNamespaceCorrection(XMLStreamReader reader, String wrongNamespace, String correctNamespace) {
super(reader);
this.wrongNamespace = wrongNamespace;
this.correctNamespace = correctNamespace;
}
@Override
public String getAttributeNamespace(int arg0) {
// System.out.println("--------------------------\n");
// System.out.println("arg0: " + arg0);
// System.out.println("getAttributeName: " + getAttributeName(arg0));
// System.out.println("super.getAttributeNamespace: " + super.getAttributeNamespace(arg0));
// System.out.println("getAttributeLocalName: " + getAttributeLocalName(arg0));
// System.out.println("getAttributeType: " + getAttributeType(arg0));
// System.out.println("getAttributeValue: " + getAttributeValue(arg0));
// System.out.println("getAttributeValue(correctNamespace, LN):"
// + getAttributeValue(correctNamespace, getAttributeLocalName(arg0)));
// System.out.println("getAttributeValue(wrongNamespace, LN):"
// + getAttributeValue(wrongNamespace, getAttributeLocalName(arg0)));
String origNamespace = super.getAttributeNamespace(arg0);
boolean replace = (((wrongNamespace == null) && (origNamespace == null))
|| ((wrongNamespace != null) && wrongNamespace.equals(origNamespace)));
return replace ? correctNamespace : origNamespace;
}
@Override
public String getNamespaceURI() {
// System.out.println("getNamespaceCount(): " + getNamespaceCount());
// for (int i = 0; i < getNamespaceCount(); i++) {
// System.out.println(i + ": " + getNamespacePrefix(i));
// }
//
// System.out.println("super.getNamespaceURI: " + super.getNamespaceURI());
String origNamespace = super.getNamespaceURI();
boolean replace = (((wrongNamespace == null) && (origNamespace == null))
|| ((wrongNamespace != null) && wrongNamespace.equals(origNamespace)));
return replace ? correctNamespace : origNamespace;
}
}
用法:
InputStream is = new FileInputStream(xmlFile);
XMLStreamReader xsr = XMLInputFactory.newFactory().createXMLStreamReader(is);
XMLReaderWithNamespaceCorrection xr =
new XMLReaderWithNamespaceCorrection(xsr, "http://wrong.namespace.uri", "http://correct.namespace.uri");
rootJaxbElem = (JAXBElement<SqgRootType>) um.unmarshal(xr);
handleSchemaError(rootJaxbElem, pmRes);
xmlns
属性(在一个或多个元素上),而有些则没有。@lunicon的解决方案使我可以阅读两种样式。