Treeview中的Javafx XML Parser区分<xxx> abc </xxx>和<abc>

时间:2018-02-09 17:54:10

标签: xml javafx treeview selection

我尝试将以下XML文件解析为树视图。

工作正常。

Treeview

但我需要区分&#34; Endnodes&#34;用鼠标选择节点时的空节点。

我尝试使用selectedItem.getChildren()。size()和selectedItem.isLeaf()。

但两者都为&#34; endnode&#34;提供了相同的结果。空的&#34;测试&#34;节点。

不幸的是我无法更改xml结构,因为ist是自动生成的。

有人可以帮帮我吗?

<root>
  <type_1>
    <element_1>
        EndNode
    </element_1>
  </type_1>
  <type_2>
    <element_2>
       <test>Endnode</test>
       <test/>
    </element_2>
  </type_2>
</root>

我从我在SO找到的一个例子开始,并根据我的需要对其进行修改。

JavaFX的

 public static TreeItem<String> readData(File file) throws SAXException, ParserConfigurationException, IOException {
    SAXParserFactory parserFactory = SAXParserFactory.newInstance();
    SAXParser parser = parserFactory.newSAXParser();
    XMLReader reader = parser.getXMLReader();
    TreeItemCreationContentHandler contentHandler = new TreeItemCreationContentHandler();

    // parse file using the content handler to create a TreeItem representation
    reader.setContentHandler(contentHandler);
    reader.parse(file.toURI().toString());

    // use first child as root (the TreeItem initially created does not contain data from the file)
    TreeItem<String> item = contentHandler.item.getChildren().get(0);

    contentHandler.item.getChildren().clear();
    return item;
}

@Override
public void initialize(URL url, ResourceBundle rb) {

    TreeItem<String> root;
    try {
        root = readData(new File("test.xml"));
        treeType.setRoot(root);
    } catch (Exception ex) {
    }
    treeType.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<String>>() {

        @Override
        public void changed(
                ObservableValue<? extends TreeItem<String>> observable, TreeItem<String> old_val,
                TreeItem<String> new_val
        ) {
            TreeItem<String> selectedItem = new_val;
            System.out.println(selectedItem.getChildren().size() + " : " + selectedItem.isLeaf());

        }

    }
    );

-

class TreeItemCreationContentHandler extends DefaultHandler {

  public TreeItem<String> item = new TreeItem<>();

  @Override
  public void endElement(String uri, String localName, String qName) throws SAXException {
      this.item = this.item.getParent();
  }

  @Override
  public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
      TreeItem<String> item = new TreeItem<>(qName);
      this.item.getChildren().add(item);
      this.item = item;
  }

  @Override
  public void characters(char[] ch, int start, int length) throws SAXException {

      String s = String.valueOf(ch, start, length).trim();
      if (!s.isEmpty()) {
          // add text content as new child
          this.item.getChildren().add(new TreeItem<>(s));
      }
  }

  }

附加组件:   我也很乐意提示如何自动突出显示终端节点。

1 个答案:

答案 0 :(得分:1)

通过使用TreeView<String>,您无法区分元素和字符数据。相反,定义一个封装它的类(或多个类)。 E.g。

public class XMLNode {

    private final String text ;
    private final boolean characterData ;

    public XMLNode(String text, boolean isCharacterData) {
        this.text = text ;
        this.characterData = isCharacterData ;
    }

    public boolean isElement() {
        return ! characterData ;
    }

    public boolean isCharacterData() {
        return characterData ;
    }

    @Override
    public boolean equals(Object obj) {
        if (! (obj instanceof XMLNode)) {
            return false ;
        }
        XMLNode other = (XMLNode) obj ;
        return other.characterData == characterData 
            && Objects.equals(other.text, text);
    }

    @Override
    public int hashCode() {
        return Objects.hash(text, characterData);
    }

    @Override
    public String toString() {
        return text ;
    }

}

现在,您可以将TreeView<String>TreItem<String>更改为TreeView<XMLNode>TreeItem<XMLNode>,然后执行

class TreeItemCreationContentHandler extends DefaultHandler {

  public TreeItem<XMLNode> item = new TreeItem<>();

  @Override
  public void endElement(String uri, String localName, String qName) throws SAXException {
      this.item = this.item.getParent();
  }

  @Override
  public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
      TreeItem<XMLNode> item = new TreeItem<>(new XMLNode(qName, false));
      this.item.getChildren().add(item);
      this.item = item;
  }

  @Override
  public void characters(char[] ch, int start, int length) throws SAXException {

      String s = String.valueOf(ch, start, length).trim();
      if (!s.isEmpty()) {
          // add text content as new child
          this.item.getChildren().add(new TreeItem<>(new XMLNode(s, true)));
      }
  }

}

现在您可以在选择处理程序中区分:

treeType.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
    XMLNode node = newSelection.getValue();
    if (node.isCharacterData()) {
        // ...
    }
    if (node.isElement()) {
        // ...
    }
});

如果要突出显示字符节点(或以其他方式显示它们),您只需要一个自定义单元格:

treeType.setCellFactory(tv -> new TreeCell<XMLNode>() {
    @Override
    protected void updateItem(XMLNode item, boolean empty) {
        super.updateItem(item, empty) ;
        setStyle("");
        if (empty) {
            setText("");
        } else {
            setText(item.toString());
            if (item.isCharacterData()) {
                setStyle("-fx-background-color: yellow;");
            }
        }
    }
});

这是一个SSCCE:

import java.io.IOException;
import java.io.StringReader;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.stage.Stage;

public class XMLTree extends Application {

    private final String xml = "<root>\n" + 
            "  <type_1>\n" + 
            "    <element_1>\n" + 
            "        EndNode\n" + 
            "    </element_1>\n" + 
            "  </type_1>\n" + 
            "  <type_2>\n" + 
            "    <element_2>\n" + 
            "       <test>Endnode</test>\n" + 
            "       <test/>\n" + 
            "    </element_2>\n" + 
            "  </type_2>\n" + 
            "</root>";

    @Override
    public void start(Stage primaryStage) throws Exception {
        TreeView<XMLNode> treeType = new TreeView<>();
        treeType.setRoot(readData(xml));
        treeType.setCellFactory(tv -> new TreeCell<XMLNode>() {
            @Override
            protected void updateItem(XMLNode item, boolean empty) {
                super.updateItem(item, empty) ;
                setStyle("");
                if (empty) {
                    setText("");
                } else {
                    setText(item.toString());
                    if (item.isCharacterData()) {
                        setStyle("-fx-background-color: yellow;");
                    }
                }
            }
        });

        treeType.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
            XMLNode node = newSelection.getValue();
            if (node.isCharacterData()) {
                System.out.println("Selected characters: "+node);
            }
            if (node.isElement()) {
                System.out.println("Selected element: <"+node+">");
            }
        });

        Scene scene = new Scene(treeType);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static TreeItem<XMLNode> readData(String data) throws SAXException, ParserConfigurationException, IOException {
        SAXParserFactory parserFactory = SAXParserFactory.newInstance();
        SAXParser parser = parserFactory.newSAXParser();
        XMLReader reader = parser.getXMLReader();
        TreeItemCreationContentHandler contentHandler = new TreeItemCreationContentHandler();

        // parse file using the content handler to create a TreeItem representation
        reader.setContentHandler(contentHandler);
        reader.parse(new InputSource(new StringReader(data)));

        // use first child as root (the TreeItem initially created does not contain data from the file)
        TreeItem<XMLNode> item = contentHandler.item.getChildren().get(0);

        contentHandler.item.getChildren().clear();
        return item;
    }

    public static class TreeItemCreationContentHandler extends DefaultHandler {

        public TreeItem<XMLNode> item = new TreeItem<>();

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            this.item = this.item.getParent();
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            TreeItem<XMLNode> item = new TreeItem<>(new XMLNode(qName, false));
            this.item.getChildren().add(item);
            this.item = item;
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {

            String s = String.valueOf(ch, start, length).trim();
            if (!s.isEmpty()) {
                // add text content as new child
                this.item.getChildren().add(new TreeItem<>(new XMLNode(s, true)));
            }
        }

        }

    public static void main(String[] args) {
        launch(args);
    }
}

enter image description here

注意:我不熟悉DOM类(虽然我使用它们时发现它不是一个非常容易使用的API);因此,可以使用现有的API而不是自定义的XMLNode类。这应该会给你这个想法。