<xml-fragment xmlns:main="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision" applyAlignmentFormats="0" applyBorderFormats="0" applyFontFormats="0" applyNumberFormats="0" applyPatternFormats="0" applyWidthHeightFormats="1" cacheId="10" createdVersion="7" dataCaption="值" indent="0" itemPrintTitles="1" mc:Ignorable="xr" minRefreshableVersion="3" multipleFieldFilters="0" name="数据透视表4" outline="1" outlineData="1" updatedVersion="7" useAutoFormatting="1" xr:uid="{D9607E40-9B8D-4658-BCDC-DB4EC965AED8}"> Javapoi中获取xr:uid
时间: 2025-08-16 09:57:31 浏览: 2
<think>问题:在Java POI中,如何从XML片段中获取`xr:uid`属性?
给定XML片段:
```xml
<xml-fragment xmlns:main="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision"
...
xr:uid="{D9607E40-9B8D-4658-BCDC-DB4EC965AED8}">
```
我们需要提取`xr:uid`属性的值。
解决方案:
由于该XML片段属于Office Open XML (OOXML)格式,我们可以使用Apache POI的OOXML库,特别是`org.apache.poi.ooxml`中的相关类来解析XML并获取属性。
具体步骤:
1. 将XML片段作为字符串或输入流传递给一个XML解析器。
2. 使用POI的`org.apache.poi.ooxml.util.DocumentHelper`将字符串转换为DOM Document。
3. 使用XPath或直接遍历节点来获取属性值。
注意:`xr:uid`是一个命名空间限定的属性,因此我们需要在获取属性时指定命名空间。
代码实现:
假设我们有一个包含上述XML片段的字符串,我们可以这样解析:
```java
import org.apache.poi.ooxml.util.DocumentHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import java.io.ByteArrayInputStream;
public class ExtractXrUid {
public static void main(String[] args) throws Exception {
String xmlFragment = "<xml-fragment xmlns:main=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" " +
"xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" " +
"xmlns:xr=\"http://schemas.microsoft.com/office/spreadsheetml/2014/revision\" " +
"applyAlignmentFormats=\"0\" applyBorderFormats=\"0\" applyFontFormats=\"0\" applyNumberFormats=\"0\" " +
"applyPatternFormats=\"0\" applyWidthHeightFormats=\"1\" cacheId=\"10\" createdVersion=\"7\" " +
"dataCaption=\"值\" indent=\"0\" itemPrintTitles=\"1\" mc:Ignorable=\"xr\" " +
"minRefreshableVersion=\"3\" multipleFieldFilters=\"0\" name=\"数据透视表4\" outline=\"1\" " +
"outlineData=\"1\" updatedVersion=\"7\" useAutoFormatting=\"1\" " +
"xr:uid=\"{D9607E40-9B8D-4658-BCDC-DB4EC965AED8}\"/>";
// 将字符串转换为Document对象
Document document = DocumentHelper.readDocument(new ByteArrayInputStream(xmlFragment.getBytes()));
// 获取根元素
Element root = document.getDocumentElement();
// 方法1:直接通过属性名(带命名空间)获取
// 注意:由于属性使用了命名空间前缀,我们需要使用getAttributeNS方法,并指定命名空间URI和本地名
String xrNamespace = "http://schemas.microsoft.com/office/spreadsheetml/2014/revision";
String uidValue = root.getAttributeNS(xrNamespace, "uid");
System.out.println("直接获取: " + uidValue);
// 方法2:使用XPath
XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();
// 设置命名空间上下文(因为xr是前缀,我们需要在XPath中处理命名空间)
xpath.setNamespaceContext(new SimpleNamespaceContext() {
{
// 注册命名空间前缀和URI的映射
put("xr", xrNamespace);
}
});
// 编写XPath表达式
XPathExpression expr = xpath.compile("/xml-fragment/@xr:uid");
String result = (String) expr.evaluate(document, XPathConstants.STRING);
System.out.println("XPath获取: " + result);
}
}
```
但是,上面的`SimpleNamespaceContext`并不是Java标准库中的类,我们可以自己实现一个简单的`NamespaceContext`,或者使用其他方式。为了简化,我们可以自己实现一个:
```java
import javax.xml.namespace.NamespaceContext;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class SimpleNamespaceContext implements NamespaceContext {
private final Map<String, String> prefixToUri;
private final Map<String, String> uriToPrefix;
public SimpleNamespaceContext() {
prefixToUri = new HashMap<>();
uriToPrefix = new HashMap<>();
}
public void put(String prefix, String uri) {
prefixToUri.put(prefix, uri);
uriToPrefix.put(uri, prefix);
}
@Override
public String getNamespaceURI(String prefix) {
return prefixToUri.get(prefix);
}
@Override
public String getPrefix(String namespaceURI) {
return uriToPrefix.get(namespaceURI);
}
@Override
public Iterator<String> getPrefixes(String namespaceURI) {
return Collections.singleton(uriToPrefix.get(namespaceURI)).iterator();
}
}
```
或者,我们可以使用更简单的方法:直接遍历属性,但这样需要处理命名空间。
由于代码中已经通过`getAttributeNS`直接获取,所以方法1是最简单的。
注意:在XML中,属性`xr:uid`实际上是由命名空间`http://schemas.microsoft.com/office/spreadsheetml/2014/revision`和本地名`uid`组成。因此,使用`getAttributeNS`并传入命名空间URI和本地名即可。
如果XML片段中可能有多个节点,这里我们只处理根节点。
如果运行环境是完整的POI项目(如处理Excel文件),那么可能已经包含了相关的命名空间声明,但这里我们只处理一个片段。
输出结果应该为:
```
直接获取: {D9607E40-9B8D-4658-BCDC-DB4EC965AED8}
XPath获取: {D9607E40-9B8D-4658-BCDC-DB4EC965AED8}
```
因此,我们推荐使用直接获取属性值的方法,因为它更简单且不依赖外部XPath配置。
注意:如果XML片段中不存在`xr:uid`属性,则返回空字符串。
另外,如果XML片段不是以`xml-fragment`为根元素,那么需要根据实际节点调整。
在实际情况中,这个XML片段可能是从更大的XML文档中截取出来的,但提取属性的方法相同。
相关问题:
§§相关问题§§:
1. 在Java中如何解析带有命名空间的XML文档?
2. 使用Apache POI处理Excel文件时,如何获取数据透视表的唯一标识符(如xr:uid)?
3. 除了使用DOM,还有哪些方式可以解析XML并获取属性值?
4. 如何处理XML中的默认命名空间(无前缀)的属性?</think>### 在 Java POI 中获取 `xr:uid` 属性的解决方案
在 Apache POI 中处理 XML 片段时,可以通过 DOM 解析获取特定命名空间下的属性值。以下是获取 `xr:uid` 属性的完整代码实现:
```java
import org.apache.poi.ooxml.util.DocumentHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.xpath.*;
public class XrUidExtractor {
// XML 命名空间常量
private static final String XR_NS = "http://schemas.microsoft.com/office/spreadsheetml/2014/revision";
public static void main(String[] args) throws Exception {
// 原始 XML 片段(实际应用中可能来自文件或输入流)
String xmlFragment = "<xml-fragment xmlns:main=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" " +
"xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" " +
"xmlns:xr=\"http://schemas.microsoft.com/office/spreadsheetml/2014/revision\" " +
"applyAlignmentFormats=\"0\" applyBorderFormats=\"0\" applyFontFormats=\"0\" applyNumberFormats=\"0\" " +
"applyPatternFormats=\"0\" applyWidthHeightFormats=\"1\" cacheId=\"10\" createdVersion=\"7\" " +
"dataCaption=\"值\" indent=\"0\" itemPrintTitles=\"1\" mc:Ignorable=\"xr\" " +
"minRefreshableVersion=\"3\" multipleFieldFilters=\"0\" name=\"数据透视表4\" outline=\"1\" " +
"outlineData=\"1\" updatedVersion=\"7\" useAutoFormatting=\"1\" " +
"xr:uid=\"{D9607E40-9B8D-4658-BCDC-DB4EC965AED8}\"/>";
// 方法1:直接DOM解析
String uid1 = extractUidViaDom(xmlFragment);
System.out.println("DOM 方式获取的 UID: " + uid1);
// 方法2:使用XPath(更灵活)
String uid2 = extractUidViaXPath(xmlFragment);
System.out.println("XPath 方式获取的 UID: " + uid2);
}
// 方法1:使用DOM解析获取xr:uid
public static String extractUidViaDom(String xml) throws Exception {
Document doc = DocumentHelper.readDocument(new ByteArrayInputStream(xml.getBytes()));
Element root = doc.getDocumentElement();
// 获取所有属性节点
NamedNodeMap attributes = root.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node attr = attributes.item(i);
// 检查是否为xr:uid属性
if ("uid".equals(attr.getLocalName()) &&
XR_NS.equals(attr.getNamespaceURI())) {
return attr.getNodeValue();
}
}
return null;
}
// 方法2:使用XPath获取xr:uid
public static String extractUidViaXPath(String xml) throws Exception {
Document doc = DocumentHelper.readDocument(new ByteArrayInputStream(xml.getBytes()));
// 创建XPath处理器并设置命名空间
XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();
// 注册命名空间前缀
xpath.setNamespaceContext(new NamespaceContext() {
public String getNamespaceURI(String prefix) {
return "xr".equals(prefix) ? XR_NS : null;
}
public String getPrefix(String uri) {
return XR_NS.equals(uri) ? "xr" : null;
}
public Iterator<String> getPrefixes(String uri) {
return Collections.singleton("xr").iterator();
}
});
// 执行XPath查询
XPathExpression expr = xpath.compile("//@xr:uid");
return (String) expr.evaluate(doc, XPathConstants.STRING);
}
}
```
### 代码解释
#### 关键组成部分
1. **XML 命名空间处理**
```java
private static final String XR_NS = "http://schemas.microsoft.com/office/spreadsheetml/2014/revision";
```
- 定义了 `xr` 命名空间的 URI,用于识别属于该命名空间的属性
2. **DOM 解析方法 (`extractUidViaDom`)**
- 使用 `DocumentHelper.readDocument()` 将 XML 字符串转换为 DOM 文档
- 遍历根元素的所有属性:
```java
if ("uid".equals(attr.getLocalName()) &&
XR_NS.equals(attr.getNamespaceURI()))
```
- 通过检查属性的本地名称和命名空间 URI 来识别 `xr:uid`
3. **XPath 方法 (`extractUidViaXPath`)**
- 配置自定义 `NamespaceContext` 将前缀 `xr` 映射到命名空间 URI
- 使用 XPath 表达式 `//@xr:uid` 直接定位目标属性
- `XPathConstants.STRING` 将结果转换为字符串
#### 技术要点
1. **命名空间处理**
- XML 中 `xr:uid` 是限定名称,必须通过命名空间 URI 而非前缀识别
- 不同文档可能使用不同的前缀(如 `ns1`, `revision` 等),URI 才是唯一标识
2. **POI 的 XML 工具**
- `DocumentHelper` 是 POI 提供的实用工具类,用于处理 OOXML 文档
- 与标准 Java DOM 兼容,但针对 POI 的特殊需求进行了优化
3. **两种方法的比较**
| 方法 | 优点 | 缺点 |
|------|------|------|
| DOM 遍历 | 无需外部依赖,适合简单查询 | 代码较冗长,效率较低 |
| XPath | 语法简洁,适合复杂查询 | 需要配置命名空间上下文 |
### 输出结果
对于提供的 XML 片段,两种方法都将返回:
```
{D9607E40-9B8D-4658-BCDC-DB4EC965AED8}
```
### 实际应用场景
1. **文档版本追踪**
在审计场景中,通过 UID 追踪数据透视表的修改历史
2. **文档关联**
当多个文档相互引用时,使用 UID 建立精确的关联关系
3. **自动化测试**
验证文档生成过程中是否保留了关键标识符
阅读全文
相关推荐




















