vtd xml java_java – 如何使用VTD-XML获取ONE元素的所有命名空间声明?

这是我在大型XML处理过程中使用的类.

一些说明:

>对于任何元素,使用的xmlns声明来自此元素和所有祖先

>当元素< elem />是< elem xmlns =“x”/>的兄弟姐妹他们有不同的命名空间

>处理XML通常是基于堆栈的

码:

import java.util.Deque;

import java.util.HashMap;

import java.util.Iterator;

import java.util.LinkedList;

import java.util.Map;

import javax.xml.namespace.QName;

import org.springframework.util.xml.SimpleNamespaceContext;

import com.ximpleware.NavException;

import com.ximpleware.VTDNav;

public class VtdXmlCurrentState

{

/* Stack of QName.toString()s for the elements of processed XML - each is a full path */

private Deque qnames = new LinkedList();

/* Stack of QName.toString()s for the elements - each is a single path element and doesn't contain "/" */

private Deque names = new LinkedList();

// current depth in input document, starting from -1

private int currentDepth = -1;

// stack of namespace contexts increased during xml depth-first VTD navigation */

private Deque namespaces = new LinkedList();

// a flag for optimizing the case when there are many sibling elements without any xmlns declarations

// the case:

//

//

// ...

// it allows to effectively manage the stack and properly handles the following case:

//

//

// in which the second element should use NSContext from parent and not a copy of sibling's NSContext

private boolean lastNSContextsDifferent = false;

/**

*

*/

public VtdXmlCurrentState()

{

// first a context without any mapping

this.namespaces.push(new SimpleNamespaceContext());

// first QName is "/"

this.qnames.push("/");

this.names.push(null);

}

/**

* Name of the current element

* @return

*/

public QName currentElementName()

{

return this.names.peek();

}

/**

* Returns parent and current path for VTDNav

*

* @param nav

* @return

* @throws NavException

*/

public String[] currentXPath(VTDNav nav) throws NavException

{

// we don't check the end - autopilot handles that

int depth = nav.getCurrentDepth();

int idx = nav.getCurrentIndex();

this.handleNamespaces(nav, depth);

// determining current XPath

// name of the current element (optimization, because we're that the token is START_ELEMENT)

String elName = nav.toRawString(nav.getTokenOffset(idx), nav.getTokenLength(idx) & 0xffff);

QName qName = null;

if (elName.contains(":")) {

String[] qname = elName.split(":");

qName = new QName(this.namespaces.peek().getNamespaceURI(qname[0]), qname[1]);

} else {

qName = new QName(this.namespaces.peek().getNamespaceURI(""), elName);

}

// full name of the current element

StringBuilder sb = new StringBuilder(1024);

String fullName = null;

for (int i = 0; i <= this.currentDepth - depth; i++) {

this.qnames.pop();

this.names.pop();

}

fullName = sb.append(this.qnames.peek()).append(qName.toString()).append("/").toString();

String parentName = this.qnames.peek();

this.qnames.push(fullName);

this.names.push(qName);

this.currentDepth = depth;

return new String[] { parentName, fullName };

}

/**

* Handling element's namespaces - if there are any xmlns[:x], we must create new NSContext

*

* @param nav

* @param depth

* @throws NavException

*/

private void handleNamespaces(VTDNav nav, int depth) throws NavException

{

// are there any ns declarations?

Map _namespaces = null;

int index = nav.getCurrentIndex() + 1;

int total = nav.getTokenCount();

while (index < total) {

int type = nav.getTokenType(index);

while (type == VTDNav.TOKEN_ATTR_NAME) {

// quickly skip non-xmlns attrs

index += 2;

type = nav.getTokenType(index);

}

if (type == VTDNav.TOKEN_ATTR_NS) {

String prefix = nav.toString(index).substring(5);

if (prefix.length() > 0)

prefix = prefix.substring(1);

String namespace = nav.toString(index + 1);

if (_namespaces == null)

_namespaces = new HashMap();

_namespaces.put(prefix, namespace);

} else if (type == VTDNav.TOKEN_ATTR_VAL) {

} else {

break;

}

index++;

}

if (_namespaces != null) {

// first remove (if necessary) previous contexts from the stack - even if new element is at the same level

// (not descendant - it's sibiling), remove old, push new

for (int i = 0; i <= this.currentDepth - depth; i++)

this.namespaces.pop();

// for this element there's xmlns declaration - this element has different namespace context

// and it will be valid till the next descendant with xmlns

// previous context

SimpleNamespaceContext snc = this.namespaces.peek();

// new ...

SimpleNamespaceContext newSnc = new SimpleNamespaceContext();

// ... to which we'll copy previous declarations

for (Iterator> prefixes = snc.getBoundPrefixes(); prefixes.hasNext();) {

String pfx = (String)prefixes.next();

newSnc.bindNamespaceUri(pfx, snc.getNamespaceURI(pfx));

}

newSnc.bindNamespaceUri("", snc.getNamespaceURI(""));

// adding (overwriting!) new namespace mappings

newSnc.setBindings(_namespaces);

this.namespaces.push(newSnc);

this.lastNSContextsDifferent = true;

} else {

// current element doesn't define new namespaces - it gets them from parent element

// optimization - no new namesaces, the same level - we don't do anything!

// we only do something if we got a level up - we have to pop some ns contexts

for (int i = 0; i < this.currentDepth - depth; i++)

this.namespaces.pop();

if (this.currentDepth > depth) {

// we went up and popped() too much ns contexts - we duplicate the most recent

this.namespaces.push(this.namespaces.peek());

} else if (this.currentDepth < depth) {

// we went down - just copy

this.namespaces.push(this.namespaces.peek());

} else {

// the same level

if (this.lastNSContextsDifferent) {

this.namespaces.pop();

this.namespaces.push(this.namespaces.peek());

}

}

this.lastNSContextsDifferent = false;

}

}

}

输入XML:

使用课程:

byte[] doc = FileCopyUtils.copyToByteArray(super.createResource("dom03.xml").getInputStream());

VTDGen vtd = new VTDGen();

vtd.setDoc(doc);

vtd.parse(true);

VTDNav nav = vtd.getNav();

AutoPilot ap = new AutoPilot();

ap.bind(nav);

ap.selectElementNS("*", "*");

VtdXmlCurrentState cxp = new VtdXmlCurrentState();

ap.iterate();

assertEquals("/{urn:test:1.0}set/", cxp.currentXPath(nav)[1]);

ap.iterate();

assertEquals("/{urn:test:1.0}set/{urn:test:1.0}documents/", cxp.currentXPath(nav)[1]);

ap.iterate();

assertEquals("/{urn:test:1.0}set/{urn:test:1.0}documents/{urn:test:1.1}doc/", cxp.currentXPath(nav)[1]);

ap.iterate();

assertEquals("/{urn:test:1.0}set/{urn:test:1.0}documents/{urn:test:1.0}doc/", cxp.currentXPath(nav)[1]);

ap.iterate();

assertEquals("/{urn:test:1.0}set/{urn:test:1.0}documents/{urn:test:1.2}doc/", cxp.currentXPath(nav)[1]);

ap.iterate();

assertEquals("/{urn:test:1.0}set/{urn:test:1.0}documents/", cxp.currentXPath(nav)[1]);

ap.iterate();

assertEquals("/{urn:test:1.0}set/documents/", cxp.currentXPath(nav)[1]);

assertFalse(ap.iterate());

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值