JAXB : XML 문서를 비 정렬 화하는 동안 네임 스페이스를 무시하는 방법은 무엇입니까?
내 스키마는 네임 스페이스를 지정하지만 문서는 지정하지 않습니다. JAXB 언 마샬링 (XML-> 객체) 중에 네임 스페이스를 무시하는 가장 간단한 방법은 무엇입니까?
즉, 나는
<foo><bar></bar></foo>
대신에,
<foo xmlns="http://tempuri.org/"><bar></bar></foo>
예를 들어 SAX 필터를 사용하여 xml 문서에 네임 스페이스 를 추가 해야한다고 생각합니다 .
그것의 의미는:
- JAXB가 이벤트를 가져 오기 전에 SAX 이벤트를 가로채는 새 클래스로 ContentHandler 인터페이스를 정의하십시오.
- 컨텐츠 핸들러를 설정할 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
}
}
다음은 누군가가이를 수행하기 위해 자체 필터를 구현하는 번거 로움을 겪고 싶지 않은 경우에 대비 한 VonC 솔루션의 확장 / 편집입니다. 또한 네임 스페이스없이 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);
In my situation, I have many namespaces and after some debug I find another solution just changing the NamespaceFitler class. For my situation (just unmarshall) this work fine.
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;
}
}
}
Another way to add a default namespace to an XML Document before feeding it to JAXB is to use JDom:
- Parse XML to a Document
- Iterate through and set namespace on all Elements
- Unmarshall using a JDOMSource
Like this:
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);
}
}
}
'Program Club' 카테고리의 다른 글
| CodeIgniter의 뷰 내에 뷰를 포함하는 가장 좋은 방법 (0) | 2020.11.23 |
|---|---|
| C ++에서 std :: vector를 반환하는 효율적인 방법 (0) | 2020.11.23 |
| XmlSerializer에서 Null 값 형식을 내 보내지 못하도록합니다. (0) | 2020.11.23 |
| 자바 스크립트를 사용하여 키 코드를 문자로 변환하는 방법 (0) | 2020.11.23 |
| 돌아 가기 대신 "이동"버튼이있는 iOS 키보드 (0) | 2020.11.23 |