使用Java中的正则表达式解析XML

3
给定以下 XML 片段,我需要获取 DataElements 下每个子项的名称/值对列表。由于某些原因,无法使用 XPath 或 XML 解析器,因此我正在使用正则表达式。
<?xml version="1.0"?>
<StandardDataObject xmlns="myns">
  <DataElements>
    <EmpStatus>2.0</EmpStatus>
    <Expenditure>95465.00</Expenditure>
    <StaffType>11.A</StaffType>
    <Industry>13</Industry>
  </DataElements>
  <InteractionElements>
    <TargetCenter>92f4-MPA</TargetCenter>
    <Trace>7.19879</Trace>
  </InteractionElements>
</StandardDataObject>

我需要的输出是: [{EmpStatus:2.0}, {Expenditure:95465.00}, {StaffType:11.A}, {Industry:13}]
DataElements下的标签名称是动态的,因此无法在正则表达式中直接表示。标签名称TargetCenter和Trace是静态的,并且可以在正则表达式中使用,但如果有避免硬编码的方法,那将是更可取的。
"<([A-Za-z0-9]+?)>([A-Za-z0-9.]*?)</"

这是我构建的正则表达式,它存在一个问题,就是错误地将{Trace:719879}包含在结果中。依赖XML内部的换行符或其他明显的格式化选项不可行。

以下是我使用的Java代码的近似表示:

private static final Pattern PATTERN_1 = Pattern.compile(..REGEX..);
private List<DataElement> listDataElements(CharSequence cs) {
    List<DataElement> list = new ArrayList<DataElement>();
    Matcher matcher = PATTERN_1.matcher(cs);
    while (matcher.find()) {
        list.add(new DataElement(matcher.group(1), matcher.group(2)));
    }
    return list;
}

我该如何修改正则表达式,只包含数据元素而忽略其他内容?

5
为什么你不使用一个合适的 XML 解析器?这样会更简单,并且不需要调试正则表达式。 - S.Lott
使用正则表达式而不是仅使用XPath和XML DOM的原因是什么? - EBGreen
8个回答

51

XML不是一种常规语言。你不能使用正则表达式对它进行解析。你认为有效的表达式会在遇到嵌套标签时出错,之后即使你修复了这个问题,它也会在处理XML注释、CDATA节、处理器指令、名称空间等方面出现问题。这是行不通的,请使用XML解析器。


3
我怀疑你提供了错误的信息,以表明正则表达式不能用于对简单可靠的子集XML进行轻量级解析。 - Mocky
11
简化而可靠的XML在某个时刻会发生改变,当它改变时,你的轻量级解析器将会失败,你会回到现在的状态。你会尝试修复你的解析器,但它很快就会变成一个难以阅读的混乱代码。 - James Van Huis
21
不,我提供的是正确的信息:普通文法无法表达上下文无关文法,这在数学上是不可能的。请阅读http://en.wikipedia.org/wiki/Chomsky_hierarchy。 - Dour High Arch
14
使用正则表达式解析XML总是会导致问题。 - James Sulak
14
让我们都认同这是一个糟糕的想法,它会以泪水收场并变成一只老鼠窝。但为了让那些后来看到这个答案并认为它正确的人有所启示:使用XML解析器解析PNG文件是不可能的,而用正则表达式解析某些XML仅仅是不明智的建议。 - Mocky
显示剩余5条评论

17

如果您可以假设在DataElements标签之间的所有内容都具有"value"形式,那么这应该可以在Java中工作。即没有属性,也没有嵌套元素。

Pattern regex = Pattern.compile("<DataElements>(.*?)</DataElements>", Pattern.DOTALL);
Matcher matcher = regex.matcher(subjectString);
Pattern regex2 = Pattern.compile("<([^<>]+)>([^<>]+)</\\1>");
if (matcher.find()) {
    String DataElements = matcher.group(1);
    Matcher matcher2 = regex2.matcher(DataElements);
    while (matcher2.find()) {
        list.add(new DataElement(matcher2.group(1), matcher2.group(2)));
    } 
}

4

使用 XPath 吧!


2

你真的应该使用一个XML库来处理这个。

如果你必须使用正则表达式,为什么不分两步来做呢?先匹配 DataElements>.*?</DataElements 然后再执行现在的操作。


1

你为什么不使用一个合适的XML解析器而是使用正则表达式呢?如果使用正确的库,这将变得非常简单。


我的怀疑是,无论你采取什么方法,这都是微不足道的,而且在这种情况下我无法使用XML解析器。 - Mocky

1

很抱歉又要给你一个“不要使用正则表达式”的答案,但是真的。请使用Commons-DigesterJAXP(随Java 5+捆绑)或JAXB(随Java 6+捆绑),因为这将避免许多麻烦。


1

你应该听取每个人的意见。轻量级解析器是一个不好的想法。

然而,如果你真的那么固执,你应该能够调整你的代码来排除DataElements标签外的标签。

private static final Pattern PATTERN_1 = Pattern.compile(..REGEX..);
private static final String START_TAG = "<DataElements>";
private static final String END_TAG = "</DataElements>";
private List<DataElement> listDataElements(String input) {
    String cs = input.substring(input.indexOf(START_TAG) + START_TAG.length(), input.indexOf(END_TAG);
    List<DataElement> list = new ArrayList<DataElement>();
    Matcher matcher = PATTERN_1.matcher(cs);
    while (matcher.find()) {
        list.add(new DataElement(matcher.group(1), matcher.group(2)));
    }
    return list;
}

如果 dataelements 标签不存在,这将会失败得很惨。

再次强调,这是一个不好的想法,你很可能在未来以 bug 报告的形式重新审视这段代码。


感谢您抽出时间整理这个内容。但是Java字符串操作的方法完全不同。 - Mocky

0
尝试通过属性文件解析正则表达式,然后创建模式对象。我解决了注入XML bean时遇到的相同问题。
例如:我需要在Spring中注入解析正则表达式'(.)(D[0-9]{7}.D[0-9]{9}.D[A-Z]{3}[0-9]{4})(.)'。但它没有工作。一旦尝试在Java类中硬编码相同的正则表达式,它就可以工作了。
Pattern pattern = Pattern.compile("(.)(D[0-9]{7}.D[0-9]{9}.D[A-Z]{2}[0-9]{4})(.)"); Matcher matcher = pattern.matcher(file.getName().trim());
Next I tried to load that Reg Ex via property file while injecting it. It worked fine.

  p:remoteDirectory="${rawDailyReport.remote.download.dir}"
  p:localDirectory="${rawDailyReport.local.valid.dir}"
  p:redEx="${rawDailyReport.download.regex}"

在属性文件中,该属性定义如下。

rawDailyReport.download.regex=(.)(D[0-9]{7}\.D[0-9]{9}\.D[A-Z]{2}[0-9]{4})(.)

这是因为带有占位符的值是通过org.springframework.beans.factory.config.PropertyPlaceholderConfigurer加载的,它在内部处理了这些xml敏感字符。
谢谢, Amith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接