1
votes

Expression régulière pour les nœuds XML correspondants

J'ai une série de balises XML répétitives représentées dans une chaîne:

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

J'essaie d'utiliser regex (JAVA) pour extraire l'attribut de nom du champ, et la valeur réelle à l'intérieur le nœud Valeur. Est-ce possible en utilisant une expression régulière?

J'ai l'expression régulière suivante qui est proche, mais elle ne s'arrête pas à la première balise de fin

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


4 commentaires

Pourquoi pas xpath à la place? Rendrait votre vie beaucoup plus facile. Chaque élément est-il une chaîne distincte?


Veuillez examiner l'utilisation d'un analyseur XML et lire sur xpath. L'utilisation de regex pour analyser XML n'est généralement pas une bonne idée.


le problème est que la chaîne contient d'autres données et que le XML n'est peut-être pas bien formé


Ce n'est pas une bonne idée d'utiliser les regex, mais si vous êtes intéressé, vous pouvez vérifier ce lien. stackoverflow.com/ questions / 13241615 /…


3 Réponses :


0
votes

Lorsque vous utilisez XML, les expressions régulières ne sont pas exactement la voie à suivre lors de la recherche, mais vous devriez plutôt utiliser Xpath, ce qui résout le problème exact que vous rencontrez. Regex peut être utilisé pour cela, mais je ne recommanderais sérieusement pas de l'utiliser.

Vous pouvez apprendre xpath en quelques heures, voici un lien pour l'apprendre .

Bonne chance


0 commentaires

1
votes

Ce n'est peut-être pas la meilleure idée que nous utilisions ici des expressions régulières. Cependant, si vous le souhaitez, nous pouvons essayer d'ajouter des groupes de capture facultatifs et collecter les données souhaitées:

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);

Nous pouvons utiliser un indicateur i ici.

 entrez la description de l'image ici

Test

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);

Démo

Cet extrait est juste pour montrer que le fonctionnement des groupes de capture:

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

RegEx

Si cette expression n'était pas souhaitée, elle peut être modifiée ou changée dans regex101.com .

Circuit RegEx

jex.im permet également de visualiser les expressions.

 entrez la description de l'image ici


0 commentaires

2
votes

Comme déjà mentionné, les expressions régulières ne conviendraient pas à cette tâche, car elles sont moins lisibles et efficaces. Mais de toute façon ...

field.xml:

Field { name: foo 1, value: someVal 1 }
Field { name: foo 2, value: someVal 2 }

Solution 1: Expression régulière ( Le moche, mais façon amusante ... )

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;
  }
}

Solution 2: XPath ( Le droit , mais de manière ennuyeuse ... )

Classe de champ:

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);
    }
}

Classe ennuyeuse:

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();
}

Sortie:

<?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>


0 commentaires