Node JS:从树 json 制作平面 json

2024-01-03

我正在编写一个 node.js 脚本来组合目录中的所有 json 文件并将结果存储为新的 json 文件。我尝试在很大程度上完成这项工作,但它有一些缺陷。

A.json

[
  {
    "id": "addEmoticon1",
    "description": "Message to greet the user.",
    "defaultMessage": "Hello, {name}!"
  },
  {
    "id": "addPhoto1",
    "description": "How are youu.",
    "defaultMessage": "How are you??"
  }
]

B.json

[
  {
    "id": "close1",
    "description": "Close it.",
    "defaultMessage": "Close!"
  }
]

我最终需要的是:

result.json

{
  "addEmoticon1": "Hello, {name}!",
  "addPhoto1": "How are you??",
  "close1": "Close!"
}

我写了一个node.js脚本:

var fs = require('fs');

function readFiles(dirname, onFileContent, onError) {
  fs.readdir(dirname, function(err, filenames) {
    if (err) {
      onError(err);
      return;
    }
    filenames.forEach(function(filename) {
      fs.readFile(dirname + filename, 'utf-8', function(err, content) {
        if (err) {
          onError(err);
          return;
        }
        onFileContent(filename, content);
      });
    });
  });
}

var data = {};
readFiles('C:/node/test/', function(filename, content) {
  data[filename] = content;
  var lines = content.split('\n');
  lines.forEach(function(line) {
    var parts = line.split('"');
    if (parts[1] == 'id') {
      fs.appendFile('result.json', parts[3]+': ', function (err) {});
    }
    if (parts[1] == 'defaultMessage') {
      fs.appendFile('result.json', parts[3]+',\n', function (err) {});
    }
  });
}, function(err) {
  throw err;
});

它提取“id”和“defaultMessage”,但无法正确附加。

我得到什么:

result.json

addEmoticon1: addPhoto1: Hello, {name}!,
close1: How are you??,
Close!,

每次运行脚本时,此输出都会不同。

  • 目标 1:将项目用双引号引起来,

  • 目标 2:在顶部和末尾添加花括号

  • 目标 3:最后一个元素末尾没有逗号

  • 目标 4:每次运行脚本时输出相同


我将从完成的解决方案开始......

这个答案的最后有一个很大的解释。让我们先尝试着想一下大局。

readdirp('.')
  .fmap(filter(match(/\.json$/)))
  .fmap(map(readfilep))
  .fmap(map(fmap(JSON.parse)))
  .fmap(concatp)
  .fmap(flatten)
  .fmap(reduce(createMap)({}))
  .fmap(data=> JSON.stringify(data, null, '\t'))
  .fmap(writefilep(resolve(__dirname, 'result.json')))
  .then(filename=> console.log('wrote results to %s', filename), err=>console.error(err));

控制台输出

wrote results to /path/to/result.json

result.json(我添加了一个c.json一些数据表明这适用于 2 个以上的文件)

{
    "addEmoticon1": "Hello, {name}!",
    "addPhoto1": "How are you??",
    "close1": "Close!",
    "somethingelse": "Something!"
}

执行

I made Promise基于接口readdir and readFile and writeFile

import {readdir, readFile, writeFile} from 'fs';

const readdirp = dir=>
  new Promise((pass,fail)=>
    readdir(dir, (err, filenames) =>
      err ? fail(err) : pass(mapResolve (dir) (filenames))));

const readfilep = path=>
  new Promise((pass,fail)=>
    readFile(path, 'utf8', (err,data)=>
      err ? fail(err) : pass(data)));

const writefilep = path=> data=>
  new Promise((pass,fail)=>
    writeFile(path, data, err=>
      err ? fail(err) : pass(path)));

为了将函数映射到我们的 Promise,我们需要一个fmap公用事业。注意我们如何注意冒泡错误。

Promise.prototype.fmap = function fmap(f) {
  return new Promise((pass,fail) =>
    this.then(x=> pass(f(x)), fail));
};

这是其余的实用程序

const fmap = f=> x=> x.fmap(f);
const mapResolve = dir=> map(x=>resolve(dir,x));
const map = f=> xs=> xs.map(x=> f(x));
const filter = f=> xs=> xs.filter(x=> f(x));
const match = re=> s=> re.test(s);
const concatp = xs=> Promise.all(xs);
const reduce = f=> y=> xs=> xs.reduce((y,x)=> f(y)(x), y);
const flatten = reduce(y=> x=> y.concat(Array.isArray(x) ? flatten (x) : x)) ([]);

最后,一个可以完成您工作的自定义函数

const createMap = map=> ({id, defaultMessage})=>
  Object.assign(map, {[id]: defaultMessage});

这是c.json

[
  {
    "id": "somethingelse",
    "description": "something",
    "defaultMessage": "Something!"
  }
]

“为什么有这么多小功能?”

不管你怎么想,你都有一个很大的问题。大问题是通过组合几个小解决方案来解决的。该代码最突出的优点是每个函数都有非常独特的目的,并且对于相同的输入它总是会产生相同的结果。这意味着每个函数都可以在程序中的其他地方使用。另一个优点是较小的函数更容易阅读、推理和调试。

将所有这些与此处给出的其他答案进行比较; @BlazeSahlen 特别是。这有 60 多行代码,基本上只能用于解决这一特定问题。它甚至不会过滤掉非 JSON 文件。因此,下次您需要创建一系列读/写文件的操作时,您每次都必须重写这 60 行中的大部分。由于样板文件耗尽,它会产生大量重复的代码和难以发现的错误。以及所有手动错误处理...哇,现在就杀了我吧。他/她认为回调地狱很糟糕?哈哈,他/她刚刚又自己创造了另一个地狱圈。


所有代码放在一起...

函数(大致)按其使用顺序出现

import {readdir, readFile, writeFile} from 'fs';
import {resolve} from 'path';

// logp: Promise<Value> -> Void
const logp = p=> p.then(x=> console.log(x), x=> console.err(x));

// fmap : Promise<a> -> (a->b) -> Promise<b>
Promise.prototype.fmap = function fmap(f) {
  return new Promise((pass,fail) =>
    this.then(x=> pass(f(x)), fail));
};

// fmap : (a->b) -> F<a> -> F<b>
const fmap = f=> x=> x.fmap(f);

// readdirp : String -> Promise<Array<String>>
const readdirp = dir=>
  new Promise((pass,fail)=>
    readdir(dir, (err, filenames) =>
      err ? fail(err) : pass(mapResolve (dir) (filenames))));

// mapResolve : String -> Array<String> -> Array<String>
const mapResolve = dir=> map(x=>resolve(dir,x));

// map : (a->b) -> Array<a> -> Array<b>
const map = f=> xs=> xs.map(x=> f(x));

// filter : (Value -> Boolean) -> Array<Value> -> Array<Value>
const filter = f=> xs=> xs.filter(x=> f(x));

// match : RegExp -> String -> Boolean
const match = re=> s=> re.test(s);

// readfilep : String -> Promise<String>
const readfilep = path=>
  new Promise((pass,fail)=>
    readFile(path, 'utf8', (err,data)=>
      err ? fail(err) : pass(data)));

// concatp : Array<Promise<Value>> -> Array<Value>
const concatp = xs=> Promise.all(xs);

// reduce : (b->a->b) -> b -> Array<a> -> b
const reduce = f=> y=> xs=> xs.reduce((y,x)=> f(y)(x), y);

// flatten : Array<Array<Value>> -> Array<Value>
const flatten = reduce(y=> x=> y.concat(Array.isArray(x) ? flatten (x) : x)) ([]);

// writefilep : String -> Value -> Promise<String>
const writefilep = path=> data=>
  new Promise((pass,fail)=>
    writeFile(path, data, err=>
      err ? fail(err) : pass(path)));

// -----------------------------------------------------------------------------

// createMap : Object -> Object -> Object
const createMap = map=> ({id, defaultMessage})=>
  Object.assign(map, {[id]: defaultMessage});

// do it !
readdirp('.')
  .fmap(filter(match(/\.json$/)))
  .fmap(map(readfilep))
  .fmap(map(fmap(JSON.parse)))
  .fmap(concatp)
  .fmap(flatten)
  .fmap(reduce(createMap)({}))
  .fmap(data=> JSON.stringify(data, null, '\t'))
  .fmap(writefilep(resolve(__dirname, 'result.json')))
  .then(filename=> console.log('wrote results to %s', filename), err=>console.error(err));

仍然遇到困难吗?

一开始要了解这些东西是如何工作的并不容易。这是一个特别奇怪的问题,因为数据嵌套得非常快。值得庆幸的是,这并不意味着我们的代码必须是一大堆嵌套混乱才能解决问题!请注意,即使我们正在处理诸如 JSON Promises 数组的 Promise 之类的事情,代码仍然保持良好且平坦的状态...

// Here we are reading directory '.'
// We will get a Promise<Array<String>>
// Let's say the files are 'a.json', 'b.json', 'c.json', and 'run.js'
// Promise will look like this:
// Promise<['a.json', 'b.json', 'c.json', 'run.js']>
readdirp('.')

  // Now we're going to strip out any non-JSON files
  // Promise<['a.json', 'b.json', 'c.json']>
  .fmap(filter(match(/\.json$/)))

  // call `readfilep` on each of the files
  // We will get <Promise<Array<Promise<JSON>>>>
  // Don't freak out, it's not that bad!
  // Promise<[Promise<JSON>, Promise<JSON>. Promise<JSON>]>
  .fmap(map(readfilep))

  // for each file's Promise, we want to parse the data as JSON
  // JSON.parse returns an object, so the structure will be the same
  // except JSON will be an object!
  // Promise<[Promise<Object>, Promise<Object>, Promise<Object>]>
  .fmap(map(fmap(JSON.parse)))

  // Now we can start collapsing some of the structure
  // `concatp` will convert Array<Promise<Value>> to Array<Value>
  // We will get
  // Promise<[Object, Object, Object]>
  // Remember, we have 3 Objects; one for each parsed JSON file
  .fmap(concatp)

  // Your particular JSON structures are Arrays, which are also Objects
  // so that means `concatp` will actually return Promise<[Array, Array, Array]
  // but we'd like to flatten that
  // that way each parsed JSON file gets mushed into a single data set
  // after flatten, we will have
  // Promise<Array<Object>>
  .fmap(flatten)

  // Here's where it all comes together
  // now that we have a single Promise of an Array containing all of your objects ...
  // We can simply reduce the array and create the mapping of key:values that you wish
  // `createMap` is custom tailored for the mapping you need
  // we initialize the `reduce` with an empty object, {}
  // after it runs, we will have Promise<Object>
  // where Object is your result
  .fmap(reduce(createMap)({}))

  // It's all downhill from here
  // We currently have Promise<Object>
  // but before we write that to a file, we need to convert it to JSON
  // JSON.stringify(data, null, '\t') will pretty print the JSON using tab to indent
  // After this, we will have Promise<JSON>
  .fmap(data=> JSON.stringify(data, null, '\t'))

  // Now that we have a JSON, we can easily write this to a file
  // We'll use `writefilep` to write the result to `result.json` in the current working directory
  // I wrote `writefilep` to pass the filename on success
  // so when this finishes, we will have
  // Promise<Path>
  // You could have it return Promise<Void> like writeFile sends void to the callback. up to you.
  .fmap(writefilep(resolve(__dirname, 'result.json')))

  // the grand finale
  // alert the user that everything is done (or if an error occurred)
  // Remember `.then` is like a fork in the road:
  // the code will go to the left function on success, and the right on failure
  // Here, we're using a generic function to say we wrote the file out
  // If a failure happens, we write that to console.error
  .then(filename=> console.log('wrote results to %s', filename), err=>console.error(err));

全做完了 !

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Node JS:从树 json 制作平面 json 的相关文章

  • 使用 JSON 文件动态更新 HTML 内容?

    我想创建一个 JS 循环 使用 jQuery 来查看 JSON 文件 并根据是否 div ids 与 JSON id 值匹配 这需要易于扩展并且无论有多少人都可以工作 div 添加了盒子 我有一个 HTML 文件 设置如下 div clas
  • 如何在 Google App Engine 上部署 1 个实例

    我需要在 Google App Engine 上部署一个简单 Node js 应用程序的 1 个实例 无需任何形式的扩展 我试过做gcloud preview app deploy 但是即使在我尝试关闭它们之后 也会创建许多实例 我的目标是
  • 将命令行参数传递给 npm 'pre' 脚本和具有多个命令的脚本

    有没有办法将命令行参数传递给 npm pre 脚本或运行多个命令的脚本 假设一个简单的脚本mySexyScript js这只是注销 process argv console log process argv 这有效 使用 npm 脚本 sc
  • 使用 npm 作为构建工具连接文件

    我最近发现我可以使用 npm 作为任务运行程序 而不是 gulp 或 grunt 到目前为止 一切都很棒 lint stylus jade uglify watch 等 但串联部分 我似乎无法实现 gulp 是这样的 gulp task s
  • 如果 Row1 = 值 1,则更新其他行

    我有一个小的 php 脚本 用于访问 mySql 数据库 我想在数据库中插入新记录之前查看该数字 值 1 是否等于数据库中的记录 这也在第 1 行 所以我想 查看传入的电话号码是否等于数据库中的电话号码 如果是这样 则必须保持电话号码相同的
  • BigQuery 如何获取 JSON 结构中的值的总和?

    我有以下查询 SELECT JSON EXTRACT json Weights as weight from select Weights blue 1 0 purple 0 0 yellow 1 0 green 1 0 as json 返
  • EJS在JS onload函数中访问express变量

    我知道你可以像这样获取 ejs 文件中变量的值 h1 h1 如果我要在同一个 ejs 页面的 onload javascript 函数中使用相同的标题变量 我将如何使用它 例如 这个函数产生一个控制台错误说 未捕获的语法错误 意外的标识符
  • 在 Django 模板中通过键访问字典

    我正在将字典从我的视图传递到模板 所以 key1 value1 key2 value2 传入并循环键 值对很好 但是我还没有找到从特定键直接在视图中访问的优雅解决方案 例如 key1 例如 bu json items key1 我可以使用一
  • 将MongoDb atlas数据库导出到本机Mongo compass

    我在 Atlas 中有一个名为 test 的远程数据库 我想将集合名称 image table 下载为 JSON 文件 在 Mac 终端中 mongoexport db test collection image table image j
  • 如何将 NODE_EXTRA_CA_CERTS 的值传递给使用 Serverless 部署的 AWS Lambda?

    我正在部署一个节点AWS Lambda https aws amazon com lambda with 无服务器 https github com serverless serverless 由于运行此代码的机构的内部要求 我需要通过额外
  • 为什么 JavaScript base-36 转换看起来不明确

    我目前正在编写一段使用 Base 36 编码的 JavaScript 我遇到了这个问题 parseInt welcomeback 36 toString 36 看来要回归了 welcomebacg 我在 Chrome 开发者控制台和 Nod
  • 如何让 Node.js 作为后台进程运行并且永不死掉?

    我通过 putty SSH 连接到 linux 服务器 我尝试将其作为后台进程运行 如下所示 node server js 然而 2 5 小时后 终端变得不活动 进程终止 即使终端断开连接 我是否也可以使进程保持活动状态 Edit 1 事实
  • 将 MOXy 设置为 JAXB 提供程序,而在同一包中没有属性文件

    我正在尝试使用 MOXy 作为我的 JAXB 提供程序 以便将内容编组 解组到 XML JSON 中 我创建了 jaxb properties 文件 内容如下 javax xml bind context factory org eclip
  • 在javascript中解析json - 长数字被四舍五入

    我需要解析一个包含长数字的 json 在 java servlet 中生成 问题是长数字被四舍五入 当执行这段代码时 var s x 6855337641038665531 var obj JSON parse s alert obj x
  • 使用 AsyncTask 传递值

    我一直在努力解决这个问题 但我已经到了不知道该怎么办的地步 我想做的是使用一个类下载文件并将其解析为字符串 然后将该字符串发送到另一个类来解析 JSON 内容 所有部件都可以单独工作 并且我已经单独测试了所有部件 我只是不知道如何将值发送到
  • aws - 将字符串作为文件上传到 S3 存储桶

    我尝试使用适用于 NodeJS 的 AWS 开发工具包将字符串作为文件保存到 AWS S3 存储桶 PUT 请求成功 但文件未在 S3 存储桶中创建 以下是我的代码片段 const s3 new S3 apiVersion 2006 03
  • Angular 2+ 安全性;保护服务器上的延迟加载模块

    我有一个 Angular 2 应用程序 用户可以在其中输入个人数据 该数据在应用程序的另一部分进行分析 该部分仅适用于具有特定权限的人员 问题是我们不想让未经授权的人知道how我们正在分析这些数据 因此 如果他们能够在应用程序中查看模板 那
  • 创建一个 JSON 对象以在 Spring Boot 测试中发布

    我想编写基本测试来使用 JSON 负载在 users URL 上执行 POST 请求来创建用户 我找不到如何将新对象转换为 JSON 到目前为止有这么多 这显然是错误的 但解释了目的 Test public void createUser
  • 在 Android 应用程序资源中使用 JSON 文件

    假设我的应用程序的原始资源文件夹中有一个包含 JSON 内容的文件 我如何将其读入应用程序 以便我可以解析 JSON See 开放原始资源 http developer android com reference android conte
  • 使用 powershell 将 XML 转换为特定的 JSON 结构

    需要有关将 xml 转换为特定 json 结构的帮助 XML 看起来像这样

随机推荐