Регулярное выражение для сопоставления узлов XML - PullRequest
0 голосов
/ 21 мая 2019

У меня есть серия повторяющихся тегов XML, представленных в строке:

<Field name="foo" date="20170501">
   <Value type="foo">someVal</Value>
</Field>
<Field name="foo" date="20170501">
   <Value type="foo">someVal</Value>
</Field>

Я пытаюсь использовать регулярное выражение (JAVA) для извлечения атрибута name из поля и фактического значения внутри узла Value. Возможно ли это с помощью регулярных выражений?

У меня есть следующее регулярное выражение, которое близко, но оно не останавливается на первом конце </Field> tag

\\<Field([^\\>]*)\\>(.+)\\</Field\\>

Ответы [ 3 ]

2 голосов
/ 21 мая 2019

Как уже упоминалось, регулярные выражения не подходят для этой задачи, поскольку они менее читабельны и эффективны.Но в любом случае ...

field.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Fields>
    <Field name="foo 1" date="20170501">
        <Value type="foo">someVal 1</Value>
    </Field>
    <Field name="foo 2" date="20170501">
        <Value type="foo">someVal 2</Value>
    </Field>
</Fields>

Решение 1 .: Регулярное выражение ( Уродливый, но забавный способ... )

try {
    byte[] encoded = Files.readAllBytes(Paths.get("path/to/fields/xml/file.xml"));
    String content = new String(encoded, StandardCharsets.UTF_8);

    Pattern pattern = Pattern.compile("<field[\\s\\S]*?name=\"(?<gName>[\\s\\S]*?)\"[\\s\\S]*?>[\\s\\S]*?<value\\b[\\s\\S]*?>(?<gVal>[\\s\\S]*?)</value>[\\s\\S]*?</field>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE );
    Matcher matcher = pattern.matcher(content);

    // while loop for each <Field> entry
    while(matcher.find()) {
        matcher.group("gName"); // named group 'gName' contains the value of name attribute
        matcher.group("gVal"); // named group 'gVal' contains the text content of the value tag
    }
} catch (IOException e) {
   e.printStackTrace();
}

Решение 2: XPath ( Правильный, но скучный путь ... )

Класс поля:

public class Field {
    private String name;
    private String value;

    // ... getter & setters ...

    @Override
    public String toString() {
        return String.format("Field { name: %s, value: %s }", this.name, this.value);
    }
}

Класс расточки:

import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Boring {
  public static void main(String[] args) {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setNamespaceAware(true);
      DocumentBuilder builder;
      Document doc = null;

      try {
          builder = factory.newDocumentBuilder();
          doc = builder.parse("path/to/fields/xml/file.xml");

          XPathFactory xpathFactory = XPathFactory.newInstance();

          // Create XPath object
          XPath xpath = xpathFactory.newXPath();

          List<Field> fields = getFields(doc, xpath);

          for (Field f : fields) {
            System.out.println(f);
          }

      } catch (Exception e) {
          e.printStackTrace();
      }
  }

  private static List<Field> getFields(Document doc, XPath xpath) {
      List<Field> list = new ArrayList<>();
      try {
          XPathExpression expr = xpath.compile("/Fields/*");

          NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
          for (int i = 0; i < nodes.getLength(); i++) {
              Node fieldNode = nodes.item(i);
              NodeList fieldNodeChildNodes = fieldNode.getChildNodes();

              Field field = new Field();
              // set name
              field.setName(fieldNode.getAttributes().getNamedItem("name").getNodeValue());

              for (int j = 0; j < fieldNodeChildNodes.getLength(); j++) {
                  if (fieldNodeChildNodes.item(j).getNodeName() == "Value") {
                      // set value
                      field.setValue(fieldNodeChildNodes.item(j).getTextContent());
                      break;
                  }
              }
              list.add(field);
          }
      } catch (XPathExpressionException e) {
          e.printStackTrace();
      }
      return list;
  }
}

Выход:

Field { name: foo 1, value: someVal 1 }
Field { name: foo 2, value: someVal 2 }
1 голос
/ 21 мая 2019

Возможно, не самая лучшая идея, что мы делаем это, используя регулярные выражения здесь. Однако, если вы хотите, мы можем попробовать добавить дополнительные группы захвата и собрать нужные нам данные:

<field name="(.+?)"(.+\s*)?<value.+?>(.+?)<\/value>(\s*)?<\/field>

Здесь мы можем использовать флаг i.

enter image description here

Тест

import java.util.regex.Matcher;
import java.util.regex.Pattern;

final String regex = "<field name=\"(.+?)\"(.+\\s*)?<value.+?>(.+?)<\\/value>(\\s*)?<\\/field>";
final String string = "<Field name=\"foo\" date=\"20170501\">\n"
     + "   <Value type=\"foo\">someVal</Value>\n"
     + "</Field>\n"
     + "<Field name=\"foo\" date=\"20170501\">\n"
     + "   <Value type=\"foo\">someVal</Value>\n"
     + "</Field>\n"
     + "<Field name=\"foo\" date=\"20170501\"><Value type=\"foo\">someVal</Value></Field>\n";
final String subst = "\\1: \\3";

final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
final Matcher matcher = pattern.matcher(string);

// The substituted value will be contained in the result variable
final String result = matcher.replaceAll(subst);

System.out.println("Substitution result: " + result);

Демо

Этот фрагмент просто показывает, как работают группы захвата:

const regex = /<field name="(.+?)"(.+\s*)?<value.+?>(.+?)<\/value>(\s*)?<\/field>/gmi;
const str = `<Field name="foo" date="20170501">
   <Value type="foo">someVal</Value>
</Field>
<Field name="foo" date="20170501">
   <Value type="foo">someVal</Value>
</Field>
<Field name="foo" date="20170501"><Value type="foo">someVal</Value></Field>
`;
const subst = `$1: $3`;

// The substituted value will be contained in the result variable
const result = str.replace(regex, subst);

console.log('Substitution result: ', result);

RegEx

Если это выражение нежелательно, его можно изменить или изменить в regex101.com .

RegEx Circuit

jex.im также помогает визуализировать выражения.

enter image description here

0 голосов
/ 21 мая 2019

При использовании XML регулярные выражения - это не совсем то, что нужно при поиске, а скорее вам следует использовать Xpath, который в значительной степени решает именно ту проблему, с которой вы столкнулись.Для этого можно использовать Regex, но я серьезно не рекомендую его использовать.

Вы можете выучить xpath через несколько часов, вот ссылка на его изучение.

Хорошоудачи

...