Reputation: 7648
We have two separated codebases that are using different styles of localization. One of the codebases is using yaml, the other is using JSON.
Right now, we're slowly migrating to the codebase with JSON but with 20k yaml strings and 7 different languages it's a pain in the ass to convert this all manually. Unfortunately we're using string notation and not object notation in our yaml files so a converter like this wouldn't work.
Example yaml
cart.title.primary: Cart
cart.title.secondary: Buy products
cart.dialog.title: Remove product
cart.dialog.text: Are you sure to remove this product?
Becomes in a converter this
{
"cart.title.primary": "Cart",
"cart.title.secondary": "Buy products",
"cart.dialog.title": "Remove product",
"cart.dialog.text": "Are you sure to remove this product?"
}
But what I want, is for each dot in the string actually an object in JSON. So ideally, the yaml I provided should become something like:
{
"cart": {
"title": {
"primary": "Cart",
"secondary: "Buy Products"
},
"dialog": {
"title": "Remove product",
"text": "Are you sure to remove this product?"
}
}
}
Is there someone with experience doing something like this? Pref. using PHP or JavaScript. Thanks in advance!
Upvotes: 1
Views: 5363
Reputation: 122057
You can build this nested structure using split
method to create path array from the keys and then reduce
method to nest properties based on that keys array.
const yaml = {
"cart.title.primary": "Cart",
"cart.title.secondary": "Buy products",
"cart.dialog.title": "Remove product",
"cart.dialog.text": "Are you sure to remove this product?"
}
const toJson = (data) => {
return Object.keys(data).reduce((a, k) => {
k.split('.').reduce((r, e, i, a) => {
return r[e] || (r[e] = (a[i + 1] ? {} : data[k]))
}, a)
return a
}, {})
}
console.log(toJson(yaml))
You can also use split
method to split the yaml string on new lines and then build the nested object with reduce
.
const yaml = `
cart.title.primary: Cart
cart.title.secondary: Buy products
cart.dialog.title: Remove product
cart.dialog.text: Are you sure to remove this product?
`
const obj = yaml.split('\n').filter(Boolean).reduce((a, k) => {
const [key, value] = k.split(': ')
key.split('.').reduce((r, e, i, arr) => {
return r[e] || (r[e] = (arr[i + 1] ? {} : value))
}, a)
return a;
}, {})
console.log(obj)
Upvotes: 0
Reputation: 4521
As a Node.js script:
#!/usr/bin/env node
const fs = require('fs')
var file = process.argv[process.argv.length - 1]
var json = {}
fs.readFileSync(file, { encoding: 'utf8' })
.split(/\r?\n/)
.forEach((line) => {
[keyPath, value] = line.split(/: */)
var target = json
var keys = keyPath.split(/\./)
var counter = 0
keys.forEach((key) => {
counter++
if (counter === keys.length) target[key] = value
else {
if (!(key in target)) target[key] = {}
target = target[key]
}
})
})
console.log(JSON.stringify(json, null, 2))
To use it:
convert.js file.yaml
Output, using your example.yaml as input:
{
"cart": {
"title": {
"primary": "Cart",
"secondary": "Buy products"
},
"dialog": {
"title": "Remove product",
"text": "Are you sure to remove this product?"
}
}
}
Upvotes: 1
Reputation: 57121
You can use a combination of the basic loading of yaml, this just assumes a string and uses yaml_parse()
, then using the code from Convert dot syntax like "this.that.other" to multi-dimensional array in PHP you can process each line at a time to create the new structure...
$yaml = 'cart.title.primary: Cart
cart.title.secondary: Buy products
cart.dialog.title: Remove product
cart.dialog.text: Are you sure to remove this product?';
$data = yaml_parse($yaml);
$output = [];
foreach ( $data as $key => $entry ) {
assignArrayByPath($output, $key, $entry);
}
function assignArrayByPath(&$arr, $path, $value, $separator='.') {
$keys = explode($separator, $path);
foreach ($keys as $key) {
$arr = &$arr[$key];
}
$arr = $value;
}
echo json_encode($output, JSON_PRETTY_PRINT);
which gives you
{
"cart": {
"title": {
"primary": "Cart",
"secondary": "Buy products"
},
"dialog": {
"title": "Remove product",
"text": "Are you sure to remove this product?"
}
}
}
Upvotes: 1