Java:读取输入 xml 并使用一些更新的属性值写入输出 xml

Java: read input xml and write output xml with some updated attribute values

我必须读取一个大 xml 文件 (A.xml) 并创建一个新的 xml 文件 (B.xml),其内容与 A.xml 除了需要在B.xml中更新的一些属性值。 例如,如果 A.xml 是:

<?xml version="1.0" encoding="utf-8"?>
<one>
    <!-- comment -->
    <a att="hello" />
</one>
<two />

我希望B.xml包含:

<?xml version="1.0" encoding="utf-8"?>
<one>
    <!-- comment -->
    <a att="UPDATED" />
</one>
<two />

我在看one solution,它使用SAX进行解析,使用PrintWriter进行编写,但它看起来很低级,我不知道是否可以复制注释并保留这种类型的关闭标签:/>。 我更喜欢流式解析器,而不是将整个文档加载到内存中,但我愿意接受建议。

对于流式解决方案,您可以使用 javax.xml.stream.XMLStreamReaderXMLEventReader 来阅读 XML 文档,更新您想要更改的任何部分,并通过管道传输 data/events 从 reader 变成 javax.xml.stream.XMLStreamWriterXMLEventWriter.

我看不出您对将 xml 文档保存在内存中不满意的原因,除非您正在处理的 xml 文件很大(100+ MB ).

我可以想到两种方法来解决这个问题:

  1. 逐个字符读取文件并更改需要更改的内容。这符合您的要求,但实施起来速度慢且难以实施。

  2. 使用 xml 解析器,找到您要查找的元素并更改它们。我倾向于这个。

第一种方法是逐字符读取 xml 文件,找到您要查找的标签,更改它们并将 xml 写入第二个文件正在做这个。这非常流线型,但是,它 xml 可以在标签中包含标签,因此可以很快变得复杂。您可以使用解析器实现此目的,但这可能涉及将文档保存在内存中。

第二个很简单。使用 xml 解析器解析文件,遍历元素,更改它们,最后,将编辑后的 ​​xml 写回文件。这涉及将文档保存在内存中,但除非您使用的是内存受限的计算机或文档很大(100+ MB),否则这不是真正的问题。

我不会在这里写出一个完整的程序,也不会给出第一种方式的例子(反正post到SO太复杂了),我给你一个起点第二种方式。

你来这里的目的:

写着Java8更新65

需要库:Dom4J 用于 xml 解析器。

public class Main {

    private static final Scanner SCANNER = new Scanner(System.in);

    /**
     * The file we're reading from.
     */
    private File inputFile;

    /**
     * The file we're writing to.
     */
    private File outputFile;

    /**
     * The attributes to replace.
     */
    private List<UserAttribute> attributes = new ArrayList<>();

    private Main() {
        getFiles();
        getReplacementTags();
    }

    private void getFiles() {
        System.out.println("Please enter the input file...");
        String input = SCANNER.nextLine();

        File inFile = new File(input);

        if (!inFile.exists() || !inFile.isFile()) {
            System.err.println("The file you entered doesn't exits or isn't a file!");
            System.exit(1);
        }

        inputFile = inFile;

        System.out.println("Please enter the output file...");
        String output = SCANNER.nextLine();

        File outFile = new File(output);

        if (!outFile.exists()) {
            try {
                outFile.createNewFile();
                System.out.println("Created file: " + outFile);
            } catch (IOException ex) {
                System.err.println("Couldn't create the output file!");
                System.exit(2);
            }
        }

        outputFile = outFile;
    }

    private void getReplacementTags() {
        System.out.println("Enter the tags you wish to replace");
        System.out.println("The format is &element name &attribute &replacement. (e.g. &one &a att &UPDATED!)");
        System.out.println("Enter a list of tags you wish to replace with each in a new line. Enter # when finished.");

        while (true) {//I'm using an infinate loop because it just seams easier to implement.
            String line = SCANNER.nextLine();

            if (line.equals("#")) {
                break;
            }

            try {
                UserAttribute attribute = getAttributeFromUserText(line);
                this.attributes.add(attribute);
                System.out.println("Added attribute replacement: " + attribute);
            } catch (IllegalArgumentException ex) {
                System.err.println("Incorrect attribute format: \n\t" + ex.getMessage());
            }
        }

        startReplacing();
    }

    private void startReplacing() {
        @SuppressWarnings("UnusedAssignment")
        Document doc = null;
        try {
            doc = new SAXReader().read(inputFile);
        } catch (DocumentException ex) {
            System.err.println("Coundn't read xml file: " + ex.getMessage());
            System.exit(3);
        }

        replaceAttributes(doc);

        try (FileWriter writer = new FileWriter(outputFile)) {
            doc.write(writer);
            System.out.println("Saved xml document to file: " + outputFile);
        } catch (IOException ex) {
            System.err.println("Couldn't write to file: " + ex.getMessage());
        }
    }

    /**
     * This does all the magic.
     *
     * You might want to fix this up as I'm sure it's rather slow. This only
     * scans 1 tag deep.
     */
    private void replaceAttributes(Document doc) {
        for (UserAttribute uattribute : attributes) {
            Element root = doc.getRootElement();

            for (Iterator i = root.elementIterator(); i.hasNext();) {
                Element element = (Element) i.next();

                if (element.getName().equals(uattribute.element)) {
                    for (Iterator i1 = element.attributeIterator(); i1.hasNext();) {
                        Attribute attribute = (Attribute) i1.next();

                        if(attribute.getName().equals(uattribute.attribute)){
                            attribute.setValue(uattribute.replacement);
                        }
                    }

                }
            }
        }
    }

    public static void main(String[] args) {
        Main m = new Main();
    }

    private static UserAttribute getAttributeFromUserText(String text) throws IllegalArgumentException {//This is a bit incomplete...
        String[] split = text.split("&");

        if (split.length != 4) {
            throw new IllegalArgumentException("Incorrect number of arguments!");
        }

        return new UserAttribute(split[1].replace(" ", ""), split[2].replace(" ", ""), split[3]);
    }

    private static final class UserAttribute {

        public final String element;

        public final String attribute;

        public final String replacement;

        public UserAttribute(String element, String attribute, String replacement) {
            this.element = element;
            this.attribute = attribute;
            this.replacement = replacement;
        }

        public String getElement() {
            return element;
        }

        public String getAttribute() {
            return attribute;
        }

        public String getReplacement() {
            return replacement;
        }

        @Override
        public String toString() {
            return String.format("{element=%s, attribute=%s, replacement=%s}", element, attribute, replacement);
        }
    }
}

A.xml

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <PersonA name="Jenny" age="22">
        <!-- A Random Comment -->
        <friends number="3">
        Friend A,
        Friend B,
        Friend C
        </friends>
    </PersonA>

    <PersonB name="Bob" age="44">
        <!-- A Random Comment... again -->
        <friends number="5">
        Friend A,
        Friend B,
        Friend C,
        Friend D,
        Friend E
        </friends>
    </PersonB>
</root>

B.xml

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <PersonA name="Joe" age="41">
        <!-- A Random Comment -->
        <friends number="3">
        Friend A,
        Friend B,
        Friend C
        </friends>
    </PersonA>

    <PersonB name="Ashley" age="32">
        <!-- A Random Comment... again -->
        <friends number="5">
        Friend A,
        Friend B,
        Friend C,
        Friend D,
        Friend E
        </friends>
    </PersonB>
</root>

参数

run:
Please enter the input file...
A.xml
Please enter the output file...
B.xml
Enter the tags you wish to replace
The format is &element name &attribute &replacement. (e.g. &one &a att &UPDATED!)
Enter a list of tags you wish to replace with each in a new line. Enter # when finished.
&PersonA &name &Joe
Added attribute replacement: {element=PersonA, attribute=name, replacement=Joe}
&PersonA &age &41
Added attribute replacement: {element=PersonA, attribute=age, replacement=41}
&PersonB &name &Ashley
Added attribute replacement: {element=PersonB, attribute=name, replacement=Ashley}
&PersonB &age &32
Added attribute replacement: {element=PersonB, attribute=age, replacement=32}
#
Saved xml document to file: B.xml
BUILD SUCCESSFUL (total time: 1 minute 32 seconds)

这几乎可以满足您的所有要求,唯一的问题是:

  1. 它有点慢,因为它必须扫描每个元素等等。
  2. 它只扫描顶级元素(即不会扫描朋友标签)
  3. 非常基础

尽管抛开问题不谈,这应该能让您抢先一步……我希望如此。

P.S。对于任何拼写错误、错误、格式不正确,我们深表歉意。我确实在很短的时间内写了这篇文章,没有做太多测试。发现不对的请评论。

您的更新用例的最佳 XML 解析器无疑是 VTD-XML... 原因如下:

  1. 它的内存效率比 DOM 高得多...vtd-xml 的 1.3x ~ 1.5x 与 DOM
  2. 的 3x~5x 相比
  3. 支持增量更新,在所有可用的XML解析库中是独一无二的。基本上,你只需要将原来的属性值"hello"剪掉,然后用"UPDATE." 没有触及文档的其他部分...

阅读this paper了解更多信息:标题为“Processing XML with Java – A Performance Benchmark ".