【问题标题】:XML stream writer library in Node.jsNode.js 中的 XML 流编写器库
【发布时间】:2018-03-02 18:57:07
【问题描述】:

我必须通过组合和处理每个大约 100MB 的多个 xml 来生成单个 xml 文件。我找不到任何可以流式写入 xml 的 node.js 库。

我试过下面的库

  1. xml 编写器
  2. xmlbuilder
  3. genx

所有这些库都将xml存储在内存中,这会导致内存不足的异常。

您对不在内存中存储完整对象的适当库有任何建议。

其中一个库(xml-writer)的示例代码

var fs = require('fs');
var XMLWriter = require('xml-writer');

var writeStream = fs.createWriteStream("myfile",{
    encoding: 'utf-8'
 });

var xw = new XMLWriter(false);
xw.startDocument('1.0', 'UTF-8').startElement(function() {
    return 'root';
});
for(var i = 0 ; i < 10000000; i++ ) {
    xw.startElement(function() {
        return 'root1';
    }).text(function() {
        return 'Some content1';
    });
    xw.endElement();
}
xw.endElement();
writeStream.write(xw.toString());
writeStream.end();

这是在文件中添加两个元素的简单代码。但这给了我内存不足的异常。因为它是创建一个对象,然后将完整的对象存储在内存中并立即将其写入文件中。

我在谷歌上搜索了很多,但找不到任何将 xml 数据流式写入文件的库。

谢谢,

【问题讨论】:

  • 您使用的是非流式 xml 生成器。试试 xml-stream 和 scramjet。两者一起会逐行输出xml。
  • @MichałCzapracki - 但 xml-stream 不是 xml 生成器。我可以使用它来解析 xml,但我还需要对它们进行一些转换,为此我试图使用上述库。如果我使用你提到的组合,我将不得不手动编写 xml 标签。
  • 我明白了,所以你想要类似于那些 json 到 xml 生成器的东西......我不确定现在是否有类似的东西,但它可以很容易地编写。我明天可以尝试寻找一些东西,如果我做对了,请告诉我。

标签: javascript node.js xml


【解决方案1】:

xmlbuilder can do this 使用“回调”API。您只需要在回调函数中写入您的流。

例如:

const xmlbuilder = require('xmlbuilder');

const xml = xmlbuilder.begin(function(chunk) { process.stdout.write(chunk); });
xml
  .dec()
  .ele('root')
;

for (let i = 0; i < 10; i++) {
  xml.ele('example' + i).up();
}

xml
  .up()  // close <root/>
  .end()
;

API 的某些部分,例如用于移动和更改已生成节点的函数 - 在此回调模式下不起作用,因为它们没有缓存在内存中。

【讨论】:

    【解决方案2】:

    xmbuilder2callback API 略有不同:

    const fs = require('fs');
    const { createCB } = require('xmlbuilder2');
    
    const ws = fs.createWriteStream('test.xml', {
      encoding: 'utf-8',
    });
    
    const xml = createCB({
      data: (text) => {
        ws.write(text);
      },
      prettyPrint: true,
    });
    xml.on('end', ws.close)
    
    xml.dec().ele('root');
    
    for (let i = 0; i < 1000000; i++) {
      xml.ele(`root${i}`).txt('Some content1').up();
    }
    
    xml
      .up()
      .end();
    

    【讨论】:

      【解决方案3】:

      如果您希望 transform stream 从对象流生成 XML,可以像这样调整 Karolis 的回答,再次使用 xmlbuilder2callback API

      const {createCB} = require('xmlbuilder2');
      const {Transform} = require('stream')
      
      class xmlTransform extends Transform {
      
        constructor({
          root="root",
          row="row",
          prettyPrint=true,
          declaration=true,
        }={}){
          super({objectMode:true})  
          this.xml = createCB({
            data: text => this.push(text),
            prettyPrint,
          })
          this.row = row
          if(declaration) {this.xml.dec({"encoding":"UTF-8"})}
          this.xml.ele(root)
        }
      
        _transform (obj, encoding, done) {
          this.xml.ele(this.row).ele(obj).up()
          done()
        }
      
        _flush(done){
          this.xml.up().end()
          done()
        }
      }
      

      可以这样测试,这里写入文件:

      const {pipeline, Readable} = require('stream')
      const fs = require('fs')
      
      pipeline(
        Readable.from([
          {a:1,b:[2,3],c:{D:"four"}},
          {a:2,b:3},
        ]),
        new xmlTransform({
          root: "myroot",
          row: "row",
          prettyPrint: true,
          declaration: true
        }),
        fs.createWriteStream("test.xml"), // or process.stdout,
        (err) => {
          if (err) console.error(err)
          console.log("done")
        }
      )
      

      ...并给出如下输出:

      <?xml version="1.0" encoding="UTF-8"?>
      <myroot>
        <row>
          <a>
            1
          </a>
          <b>
            2
          </b>
          <b>
            3
          </b>
          <c>
            <D>
              four
            </D>
          </c>
        </row>
        <row>
          <a>
            2
          </a>
          <b>
            3
          </b>
        </row>
      </myroot>
      

      您可以使用基于源 XML 文件的对象流替换测试中的 Readable.from,可能使用 xml-stream 或其他方式。

      使用像这样的转换流将生成 XML 与写入文件分开可以提供更多的灵活性,并且使用 pipeline 可以省去处理流事件的麻烦。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-07-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-03
        • 1970-01-01
        • 1970-01-01
        • 2014-11-05
        相关资源
        最近更新 更多