[英]stax - get xml node as string
xml 看起來像這樣:
<statements>
<statement account="123">
...stuff...
</statement>
<statement account="456">
...stuff...
</statement>
</statements>
我正在使用 stax 一次處理一個“ <statement>
”,並且我得到了它。 我需要將整個語句節點作為字符串獲取,以便我可以創建“123.xml”和“456.xml”,或者甚至將其加載到按帳戶索引的數據庫表中。
使用這種方法: http : //www.devx.com/Java/Article/30298/1954
我正在做這樣的事情:
String statementXml = staxXmlReader.getNodeByName("statement");
//load statementXml into database
我有一個類似的任務,雖然最初的問題超過一年,但我找不到令人滿意的答案。 到目前為止最有趣的答案是Blaise Doughan的答案,但我無法讓它在我期望的XML上運行(可能底層解析器的一些參數可能會改變它?)。 這里的XML非常簡單:
<many-many-tags>
<description>
...
<p>Lorem ipsum...</p>
Devils inside...
...
</description>
</many-many-tags>
我的解決方案
public static String readElementBody(XMLEventReader eventReader)
throws XMLStreamException {
StringWriter buf = new StringWriter(1024);
int depth = 0;
while (eventReader.hasNext()) {
// peek event
XMLEvent xmlEvent = eventReader.peek();
if (xmlEvent.isStartElement()) {
++depth;
}
else if (xmlEvent.isEndElement()) {
--depth;
// reached END_ELEMENT tag?
// break loop, leave event in stream
if (depth < 0)
break;
}
// consume event
xmlEvent = eventReader.nextEvent();
// print out event
xmlEvent.writeAsEncodedUnicode(buf);
}
return buf.getBuffer().toString();
}
用法示例:
XMLEventReader eventReader = ...;
while (eventReader.hasNext()) {
XMLEvent xmlEvent = eventReader.nextEvent();
if (xmlEvent.isStartElement()) {
StartElement elem = xmlEvent.asStartElement();
String name = elem.getName().getLocalPart();
if ("DESCRIPTION".equals(name)) {
String xmlFragment = readElementBody(eventReader);
// do something with it...
System.out.println("'" + fragment + "'");
}
}
else if (xmlEvent.isEndElement()) {
// ...
}
}
請注意,提取的XML片段將包含完整的提取的正文內容,包括空格和注釋。 為了簡潔起見,省略了按需過濾或使緩沖區大小可參數化的問題:
'
<description>
...
<p>Lorem ipsum...</p>
Devils inside...
...
</description>
'
你可以使用StAX。 您只需要將XMLStreamReader推進到start元素for語句。 檢查帳戶屬性以獲取文件名。 然后使用javax.xml.transform API將StAXSource轉換為包裝文件的StreamResult。 這將推進XMLStreamReader,然后重復此過程。
import java.io.File;
import java.io.FileReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.stream.StreamResult;
public class Demo {
public static void main(String[] args) throws Exception {
XMLInputFactory xif = XMLInputFactory.newInstance();
XMLStreamReader xsr = xif.createXMLStreamReader(new FileReader("input.xml"));
xsr.nextTag(); // Advance to statements element
while(xsr.nextTag() == XMLStreamConstants.START_ELEMENT) {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
File file = new File("out" + xsr.getAttributeValue(null, "account") + ".xml");
t.transform(new StAXSource(xsr), new StreamResult(file));
}
}
}
Stax是一種低級訪問API,它沒有查詢或遞歸訪問內容的方法。 但你真正想做什么? 你為什么要考慮Stax?
除了使用適用於XPath的樹模型(DOM,XOM,JDOM,Dom4j)之外,處理數據時的最佳選擇通常是數據綁定庫,如JAXB。 有了它,您可以傳遞Stax或SAX讀取器並要求它將xml數據綁定到Java bean中,而不是弄亂xml進程Java對象。 這通常更方便,而且通常性能相當。 只有較大文件的技巧是你不想一次綁定整個事物,而是綁定每個子樹(在你的情況下,一次一個'語句')。 這是通過迭代Stax XmlStreamReader,然后使用JAXB進行綁定來完成的。
我一直在谷歌搜索,這似乎很難。
鑒於我的xml,我認為它可能更簡單:
StringBuilder buffer = new StringBuilder();
for each line in file {
buffer.append(line)
if(line.equals(STMT_END_TAG)){
parse(buffer.toString())
buffer.delete(0,buffer.length)
}
}
private void parse(String statement){
//saxParser.parse( new InputSource( new StringReader( xmlText ) );
// do stuff
// save string
}
為什么不直接使用xpath呢?
你可以有一個相當簡單的xpath來獲取所有'statement'節點。
像這樣:
//statement
編輯#1:如果可能的話,看看dom4j 。 您可以讀取字符串並相當簡單地獲取所有“語句”節點。
編輯#2:使用dom4j,你就是這樣做的:(來自他們的食譜)
String text = "your xml here";
Document document = DocumentHelper.parseText(text);
public void bar(Document document) {
List list = document.selectNodes( "//statement" );
// loop through node data
}
我遇到了類似的問題並找到了解決方案。 我使用了@t0r0X 提出的解決方案,但它在 Java 11 的當前實現中效果xmlEvent.writeAsEncodedUnicode
,方法xmlEvent.writeAsEncodedUnicode
在結果 XML 片段中創建了起始元素(在StartElementEvent
類中)的無效字符串表示,所以我不得不修改它,但它似乎工作得很好,我可以通過 DOM 和 JaxBMarshaller 將片段解析為特定的數據容器來立即驗證。
就我而言,我有巨大的結構
<Orders>
<ns2:SyncOrder xmlns:ns2="..." xmlns:ns3="....." ....>
.....
</ns2:SyncOrder>
<ns2:SyncOrder xmlns:ns2="..." xmlns:ns3="....." ....>
.....
</ns2:SyncOrder>
...
</Orders>
在數百兆的文件中(很多重復的“SyncOrder”結構),因此使用DOM會導致大量內存消耗和緩慢評估。 因此,我使用 StAX 將巨大的 XML 拆分為較小的 XML 片段,我使用 DOM 對其進行了分析,並使用了從元素SyncOrder
的 xsd 定義生成的SyncOrder
(我從 webservice 獲得的這個基礎結構,它使用相同的結構,但是這不重要)。
在這段代碼中,可以看到 XML 片段在哪里創建並可以使用,我直接在其他處理中使用了它......
private static <T> List<T> unmarshallMultipleSyncOrderXmlData(
InputStream aOrdersXmlContainingSyncOrderItems,
Function<SyncOrderType, T> aConversionFunction) throws XMLStreamException, ParserConfigurationException, IOException, SAXException {
DocumentBuilderFactory locDocumentBuilderFactory = DocumentBuilderFactory.newInstance();
locDocumentBuilderFactory.setNamespaceAware(true);
DocumentBuilder locDocBuilder = locDocumentBuilderFactory.newDocumentBuilder();
List<T> locResult = new ArrayList<>();
XMLInputFactory locFactory = XMLInputFactory.newFactory();
XMLEventReader locReader = locFactory.createXMLEventReader(aOrdersXmlContainingSyncOrderItems);
boolean locIsInSyncOrder = false;
QName locSyncOrderElementQName = null;
StringWriter locXmlTextBuffer = new StringWriter();
int locDepth = 0;
while (locReader.hasNext()) {
XMLEvent locEvent = locReader.nextEvent();
if (locEvent.isStartElement()) {
if (locDepth == 0 && Objects.equals(locEvent.asStartElement().getName().getLocalPart(), "Orders")) {
locDepth++;
} else {
if (locDepth <= 0)
throw new IllegalStateException("There has been passed invalid XML stream intot he function. "
+ "Expecting the element 'Orders' as the root alament of the document, but found was '"
+ locEvent.asStartElement().getName().getLocalPart() + "'.");
locDepth++;
if (locSyncOrderElementQName == null) {
/* First element after the "Orders" has passed, so we retrieve
* the name of the element with the namespace prefix: */
locSyncOrderElementQName = locEvent.asStartElement().getName();
}
if(Objects.equals(locEvent.asStartElement().getName(), locSyncOrderElementQName)) {
locIsInSyncOrder = true;
}
}
} else if (locEvent.isEndElement()) {
locDepth--;
if(locDepth == 1 && Objects.equals(locEvent.asEndElement().getName(), locSyncOrderElementQName)) {
locEvent.writeAsEncodedUnicode(locXmlTextBuffer);
/* at this moment the call of locXmlTextBuffer.toString() gets the complete fragment
* of XML containing the valid SyncOrder element, but I have continued to other processing,
* which immediatelly validates the produced XML fragment is valid and passes the values
* to communication object: */
Document locDocument = locDocBuilder.parse(new ByteArrayInputStream(locXmlTextBuffer.toString().getBytes()));
SyncOrderType locItem = unmarshallSyncOrderDomNodeToCo(locDocument);
locResult.add(aConversionFunction.apply(locItem));
locXmlTextBuffer = new StringWriter();
locIsInSyncOrder = false;
}
}
if (locIsInSyncOrder) {
if (locEvent.isStartElement()) {
/* here replaced the standard implementation of startElement's method writeAsEncodedUnicode: */
locXmlTextBuffer.write(startElementToStrng(locEvent.asStartElement()));
} else {
locEvent.writeAsEncodedUnicode(locXmlTextBuffer);
}
}
}
return locResult;
}
private static String startElementToStrng(StartElement aStartElement) {
StringBuilder locStartElementBuffer = new StringBuilder();
// open element
locStartElementBuffer.append("<");
String locNameAsString = null;
if ("".equals(aStartElement.getName().getNamespaceURI())) {
locNameAsString = aStartElement.getName().getLocalPart();
} else if (aStartElement.getName().getPrefix() != null
&& !"".equals(aStartElement.getName().getPrefix())) {
locNameAsString = aStartElement.getName().getPrefix()
+ ":" + aStartElement.getName().getLocalPart();
} else {
locNameAsString = aStartElement.getName().getLocalPart();
}
locStartElementBuffer.append(locNameAsString);
// add any attributes
Iterator<Attribute> locAttributeIterator = aStartElement.getAttributes();
Attribute attr;
while (locAttributeIterator.hasNext()) {
attr = locAttributeIterator.next();
locStartElementBuffer.append(" ");
locStartElementBuffer.append(attr.toString());
}
// add any namespaces
Iterator<Namespace> locNamespaceIterator = aStartElement.getNamespaces();
Namespace locNamespace;
while (locNamespaceIterator.hasNext()) {
locNamespace = locNamespaceIterator.next();
locStartElementBuffer.append(" ");
locStartElementBuffer.append(locNamespace.toString());
}
// close start tag
locStartElementBuffer.append(">");
// return StartElement as a String
return locStartElementBuffer.toString();
}
public static SyncOrderType unmarshallSyncOrderDomNodeToCo(
Node aSyncOrderItemNode) {
Source locSource = new DOMSource(aSyncOrderItemNode);
Object locUnmarshalledObject = getMarshallerAndUnmarshaller().unmarshal(locSource);
SyncOrderType locCo = ((JAXBElement<SyncOrderType>) locUnmarshalledObject).getValue();
return locCo;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.