我有一个类似JSON文件:
{
"someRandomStuff": "myRandomStuff",
"includeNodesFromFiles": {
"item1": "item1.json",
"item2": "item2.json",
"item3": "item3.json"
}
}
现在我想用每个文件中的JSON内容替换item1,item2和item3,因此目标文件如下所示:
{
"someRandomStuff": "myRandomStuff",
"includeNodesFromFiles": {
"item1": {"whatever": "...whatever was in item1.json"},
"item2": {"whatever": "...whatever was in item2.json"},
"item3": {"whatever": "...whatever was in item3.json"},
}
}
或类似地对于数组:
{
"someRandomStuff": "myRandomStuff",
"includeNodesFromFiles": [
"item1.json",
"item2.json",
"item3.json"
]
}
收件人:
{
"someRandomStuff": "myRandomStuff",
"includeNodesFromFiles": [
{"whatever": "...whatever was in item1.json"},
{"whatever": "...whatever was in item2.json"},
{"whatever": "...whatever was in item3.json"}
]
}
我如何用Grunt做到这一点?到目前为止,我还没有找到可以立即完成的Grunt任务。
Grunt的新手,所以请忍受我。
答案 0 :(得分:2)
简短的回答:这是一个非常自定义的要求,并且我不知道现有的grunt插件可以实现这一目标。
您需要创建自己的grunt插件来处理此类要求。以下步骤描述了如何实现此目的:
首先如下创建一个插件文件。让我们将文件命名为json-replace.js
:
json-replace.js
/**
* Custom grunt plugin replaces JSON values (filepatha) with JSON file content.
*/
module.exports = function(grunt) {
'use strict';
var path = require('path');
/**
* Custom grunt multi task to replace values in JSON.
*/
grunt.registerMultiTask('jsonReplace', 'Replace values in JSON', function() {
// Read the configuration values.
var src = this.data.src;
var dest = this.data.dest;
var keyName = this.data.key;
var baseDir = path.dirname(src);
// Default options
var opts = this.options({
indent: 2
});
/**
* Determines whether the passed value is an Array.
* @param {*} value - A reference to the value to check.
* @returns {Boolean} - true if the value is an Array, otherwise false.
*/
function isArray(value) {
return Array.isArray(value);
}
/**
* Determines whether the passed value is an Object.
* @param {*} value - A reference to the value to check.
* @returns {Boolean} - true if the value is an Object, otherwise false.
*/
function isObject(value) {
return Object.prototype.toString.call(value) === '[object Object]';
}
/**
* Reads a file's contents, parsing the data as JSON.
* @param {String} srcPath - The filepath to the JSON file to parse.
* @returns {Object}- The parsed JSON data.
*/
function readJson(srcPath) {
return grunt.file.readJSON(srcPath);
}
/**
* Writes JSON data to a file.
* @param {String} destPath - A filepath for where to save the file.
* @param {Object|Array} data - Value to covert to JSON and saved to file.
* @param {Number} [indent=2] - The no. of spaces to indent the JSON.
*/
function writeJson(destPath, data, indent) {
indent = (typeof indent !== 'undefined') ? indent : 2;
grunt.file.write(destPath, JSON.stringify(data, null, indent));
grunt.log.writeln('Saved \x1b[96m1\x1b[0m file');
}
/**
* Checks whether a file exists and logs any missing files to console.
* @param {String} filePath - The filepath to check for its existence.
* @returns {Boolean} - true if the filepath exists, otherwise false.
*/
function fileExists(filePath) {
if (!grunt.file.exists(filePath)) {
grunt.fail.warn('Unable to read \"' + filePath + '\"');
return false;
}
return true;
}
/**
* Checks whether type of value is a string and logs an error if not.
* @param {*} value - The value to check
* @returns {Boolean} - true if type of value is 'string', otherwise false.
*/
function isString(value) {
if (typeof value !== 'string') {
grunt.fail.warn('Value type must be a string: found \"' + value + '\"');
return false;
}
return true;
}
/**
* Processes each Array item for a given key.
* @param {Object} data - The parsed JSON data to process.
* @param {String} keyName - Name of key whose Array values to process.
* @param {String} baseDir - Base directory path of the source json file.
*/
function processArrayItems(data, keyName, baseDir) {
var replacement = [];
data[keyName].forEach(function(item) {
var fullPath = path.join(baseDir, item);
if (isString(item) && fileExists(fullPath)) {
replacement.push(readJson(fullPath));
}
});
data[keyName] = replacement;
writeJson(dest, data, opts.indent);
}
/**
* Processes an Objects key/value pair for a given Object.
* @param {Object} data - The parsed JSON data to process.
* @param {String} keyName - Name of key whose property values to process.
* @param {String} baseDir - Base directory path of the source json file.
*/
function processObjectValues(data, keyName, baseDir) {
var replacement = {};
Object.keys(data[keyName]).forEach(function(key) {
var accessor = data[keyName][key];
var fullPath = path.join(baseDir, accessor);
if (isString(accessor) && fileExists(fullPath)) {
replacement[key] = readJson(fullPath);
}
});
data[keyName] = replacement;
writeJson(dest, data, opts.indent);
}
// Read the source JSON file
var srcData = readJson(src);
// Check if the `key` provided exists in source JSON.
if (!srcData[keyName]) {
grunt.fail.warn('Missing given key "' + keyName + '" in ' + src);
}
// Invoke the appropriate processing for key value.
if (isArray(srcData[keyName])) {
processArrayItems(srcData, keyName, baseDir);
} else if (isObject(srcData[keyName])) {
processObjectValues(srcData, keyName, baseDir);
} else {
grunt.fail.warn('Value for "' + keyName + '" must be object or array');
}
});
};
将json-replace.js
保存在项目根目录(即与custom-grunt-tasks
和Gruntfile.js
相同级别的名为package.json
的文件夹中。例如:
.
├── Gruntfile.js
├── custom-grunt-tasks <---
│ └── json-replace.js <---
├── node_modules
│ └── ...
├── package.json
└── ...
将以下任务添加到您的Gruntfile.js
:
Gruntfile.js
module.exports = function(grunt) {
grunt.loadTasks('custom-grunt-tasks');
grunt.initConfig({
jsonReplace: { // <-- Task
targetA: { // <-- Target
src: 'path/to/source.json',
dest: 'path/to/output/file.json',
key: 'includeNodesFromFiles'
}
}
// ...
});
grunt.registerTask('default', ['jsonReplace']);
}
注释:(关于上述 Gruntfile.js 的配置)
读取grunt.loadTasks('custom-grunt-tasks');
的行从名为json-replace.js
的目录中加载自定义插件(即custom-grunt-tasks
)。
名为jsonReplace
的任务被添加到grunt.initConfig({...})
,其中包含一个任意命名为targetA
的{{3}}。
src
属性的值应替换为指向您的源.json
文件的有效文件路径。
应将dest
属性的值替换为保存新.json
文件的文件路径。
key
的值应替换为有效的键名。所提供密钥的名称应针对包含.json
文件路径的对象或数组的密钥(例如,如您的两个示例中所述的includeNodesFromFiles
)
其他信息:
json-replace.js
是Target,这基本上意味着您可以根据需要在jsonReplace
任务中配置多个目标。例如:
// ...
jsonReplace: { // <-- Task
targetA: { // <-- Target
src: 'path/to/source.json',
dest: 'path/to/output/file.json',
key: 'includeNodesFromFiles'
},
targetB: { // <-- Another Target
src: 'path/to/another/source.json',
dest: 'path/to/output/another/file.json',
key: 'anotherKeyName'
}
}
// ...
如果要处理多个.json
文件,多个目标可能很有用。
json-replace.js
期望key
的值(例如includeNodesFromFiles
)为以下任一值:
.json
文件的文件路径值。或者是一个数组(如您的第二个示例所示),其中数组的每个项目都是现有.json
文件的文件路径值。
注意:如果给定JSON key
的结构与上述两个结构中的任何一个都匹配,则会向控制台记录错误。
json-replace.js
默认将生成的.json
文件的内容缩进为两个空格。但是,如果要更改此设置,可以使用indent
选项。例如,以下任务配置会将生成的.json
文件缩进四个空格:
// ...
jsonReplace: {
options: {
indent: 4 // <-- Custom indent option
},
targetA: {
src: 'path/to/source.json',
dest: 'path/to/output/file.json',
key: 'includeNodesFromFiles'
}
}
// ...
重要:在源
.json
文件(例如item1.json
,item2.json
等)中定义文件路径值时,此解决方案希望它们是相对于源.json
文件本身。
答案 1 :(得分:0)
这是一个非常简单的任务,只需加载文件并更改对象中的值(所有文件路径都与gruntfile.js
相关):
grunt.registerTask('mytask', 'My super task', () => {
// file with json with file paths
//{"someRandomStuff":"myRandomStuff","includeNodesFromFiles":{"item1":"item1.json","item2":"item2.json","item3":"item3.json"}}
let main = JSON.parse(fs.readFileSync('myjson.json', 'utf8'));
Object.keys(main.includeNodesFromFiles).forEach((key) => {
main.includeNodesFromFiles[key] = JSON.parse(fs.readFileSync(main.includeNodesFromFiles[key], 'utf8'));
});
//... do some stuff
grunt.log.writeln(JSON.stringify(main)); //{"someRandomStuff":"myRandomStuff","includeNodesFromFiles":{"item1":{},"item2":{},"item3":{}}}
});
答案 2 :(得分:0)
这是我想出的解决方案。如果文件名存在,它将用文件递归替换文件名。还接受要包含的文件的基本URL:
## </code to remove>