【问题标题】:Java: read input xml and write output xml with some updated attribute valuesJava:读取输入 xml 并使用一些更新的属性值写入输出 xml
【发布时间】:2016-06-27 00:50:46
【问题描述】:

我必须读取一个大的 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 用于编写,但它看起来相当低级,我不知道是否可以复制 cmets 并保留这种类型的关闭标签: /&gt;。 我更喜欢流式解析器而不是将整个文档加载到内存中,但我愿意接受建议。

【问题讨论】:

    标签: java xml dom sax


    【解决方案1】:

    对于流式解决方案,您可以使用 javax.xml.stream.XMLStreamReaderXMLEventReader 来读取 XML 文档,更新您想要更改的任何部分,并将来自阅读器的数据/事件通过管道传输到 javax.xml.stream.XMLStreamWriter 或 @987654324 @。

    【讨论】:

    • 谢谢,我认为XMLEventWriter 没有办法编写自闭标签,所以我可能会选择XMLStreamWriter
    【解决方案2】:

    我看不出您为什么不满意将 xml 文档保存在内存中的原因,除非您正在使用的 xml 文件很大 (100+ MB)。

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

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

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

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

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

    我不会在这里写出完整的程序,也不会给出第一种方式的示例(无论如何发布到SO都太复杂了),不过我会为您提供第二种方式的起点。

    你来这里是为了什么:

    使用 Java 8 更新 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. 非常基础

    尽管抛开问题不谈,但这应该能让你领先一步……我希望。

    附:对于任何拼写错误,大字,格式不正确,我们深表歉意。我确实在短时间内写了这个,没有做太多的测试。如果您发现有问题,请发表评论。

    【讨论】:

    • 感谢您的回答,我更倾向于使用@wero 建议的任何读者/作者
    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-25
      • 1970-01-01
      • 2021-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-17
      相关资源
      最近更新 更多