I am trying to create a local lang file that will be formatted as json. I have the following navigation in json format below. And I need to create a new JSON file using GULP to create a lang file (see below)
"lists": [
{
"title": "Application Intel",
"items": [
{
"title": "Analytics Dashboard",
"href": "intel_analytics_dashboard.html"
},
{
"title": "Marketing Dashboard",
"href": "intel_marketing_dashboard.html"
},
{
"title": "CEO Dashboard",
"href": "intel_ceo_dashboard.html"
},
{
"title": "Introduction",
"href": "intel_introduction.html"
},
{
"title": "Build Notes",
"href": "intel_build_notes.html",
"text": "Build Notes",
"span": {
"class": "",
"text": "v{{version}}"
}
}
]
}
I need to create a file that looks like the following json:
"nav": {
"application_intel": "Application Intel",
"intel_analytics_dashboard": "Analytics Dashboard",
"intel_marketing_dashboard": "Marketing Dashboard",
"intel_ceo_dashboard": "CEO Dashboard",
"intel_introduction": "Introduction",
"intel_build_notes": "Build Notes",
}
Whats the best way to go about this?
Here is solution.
Let's say you have nav.json file inside src and you want to change its shape and place it into dest directory. You can achieve this from within gulpfile.js
const { src, dest } = require("gulp");
const through = require("through2");
// gulp task
function json() {
return src("src/nav.json")
.pipe(
through.obj((file, enc, cb) => {
// get content of json file
const rawJSON = file.contents.toString();
// parse raw json into javscript object
const parsed = JSON.parse(rawJSON);
// transform json into desired shape
const transformed = transformJson(parsed);
// make string from javascript obj
const stringified = JSON.stringify(transformed, null, 2);
// make bufer from string and attach it as current file content
file.contents = Buffer.from(stringified);
// pass transformed file into next gulp pipe
cb(null, file);
})
)
.pipe(dest("dest"));
}
// transformation
function transformJson(input) {
const result = { nav: {} };
// read json field by field
Object.keys(input).forEach(topLevelKey => {
// current object
const topLevelItem = input[topLevelKey];
// in your design topLevelItems are arrays
topLevelItem.forEach(menuItem => {
if (menuItem.title) {
// make url either from item href or title
const itemUrl = makeUrl(menuItem.href || menuItem.title);
result.nav[itemUrl] = menuItem.title;
}
// prcoess children
if (menuItem.items) {
menuItem.items
.filter(child => !!child.title) // process only child items with title
.forEach(child => {
const childUrl = makeUrl(child.href || child.title);
result.nav[childUrl] = child.title;
});
}
});
});
return result;
}
// helper func
function makeUrl(href) {
return href
.toLowerCase()
.replace(/\.html$/, "")
.replace(/\s/g, "_");
}
// export for use in command line
exports.json = json;
json transformation function is bit forEachy and if you have deep nested navigation structure, maybe you should change it into something recursive
Related
I have a json file that contains content for several different pages that are under a "service" category. I use dynamic routes in nextJS by having a file as "[serviceId].tsx", this routing works. However I have a json file where I want to use the [serviceId] provided in the route to access information.
I have the following code in my [serviceId].tsx file:
const json = jsonFile.services
const router = useRouter()
const serviceId = router.query.serviceId
return (
<div>
<ArticleWithPicture title={content.title} description={content.description}/>
</div>
)
}
My json file looks similar to this (ive edited it to be more clear for this example):
{
"serviceId":
[
{
"service1": {
"id": "xx",
"title": "xxx",
"description": "xx",
"featuredCompany":
[
{ "id": "1",
"name": "xxx",
"companyPageURL": "/",
"imagePath": "xxx",
"description": "xxx",
"additionalServices": {
"service1": "xxx",
"service2": "xxx"
},
"instagramURL":"/",
"twitterURL": "/"
}
]
}
},
{
"service2": {
"id": "xxx",
"title": "xxx",
"description": "xxx",
"featuredCompany":
[
{ "id": "1",
"name": "xxx",
"companyPageURL": "/",
"imagePath": "xxx",
"description": "xxx",
"additionalServices": {
"service1": "xxx",
"service2": "xx"
},
"instagramURL":"/",
"twitterURL": "/"
}
]
}
}
]
}
Basically, each Service has the content for each indiviual page. So I want to dynamically set for instance the title of my component "ArticleWithPicture" based on the corresponding title in my json file based on the serviceId that I get from router.query.serviceId. However when I try the following code:
<ArticleWithPicture title={json.{serviceId}.title}/>
I get error (this is due to how I use "{}" within a "{}", is there a way to do this better?
But I also cannot access it if I do eg:
const title = json.serviceId.title
or (what is what I actually want to do ie: query the json file based on my serviceId provided by "router.query.serviceId")
const title = json.{serviceId}.title
I guess something might be wrong with either my json file structure or how I try to access it. Any advice would be appreciated.
Thanks!
I'm assuming the JSON you provided is your entire jsonFile.
If the JSON you provided is just jsonFile.services, then change any uses of jsonFile to jsonFile.services
and update the type.
The format of the JSON isn't great for your use case because there's a lot of unnecessary wrapping.
With your current JSON
Assuming you cannot modify the format of the JSON, you would have to find the service from the serviceId array:
function getService(json, serviceId) {
return json.serviceId
.find((serviceWrapper) => serviceWrapper[serviceId] !== undefined)
?.service;
}
A fully typed example:
type Service = {
id: string
title: string
description: string
// ...
};
// you don't have to use this, I just included it for clarity
type JsonFile = {
serviceId: {
[serviceId: string]: Service
}[]
};
function getService(json: JsonFile, serviceId: string): Service | undefined {
return json.serviceId
.find((serviceWrapper) => serviceWrapper[serviceId] !== undefined)
?.service;
}
// declare const jsonFile: JsonFile;
export default function ServicePage() {
const router = useRouter();
const serviceId = router.query.serviceId as string;
const content = getService(jsonFile, serviceId);
if (!content) return (
<div>
{'Article doesn\'t exist.'}
</div>
);
return (
<div>
<ArticleWithPicture title={content.title} description={content.description} />
</div>
);
}
With better JSON
An example JSON that would need less unwrapping is:
{
"service1": {
"id": "xx",
"title": "xxx",
// ...
},
"service2": {
"id": "xxx",
"title": "xxx",
// ...
}
}
type JsonFile = {
[serviceId: string]: Service
}
Then you would be able to just do jsonFile[serviceId] or jsonFile.services[serviceId] to get a service.
I've been trying to change inline data (which worked) to external data in a JSON file.
Originally, I had this, which worked:
const treeData = [
{
name: "Parent"
attributes: {
id: 12345678
},
children: [
{
name: "Child"
attributes: {
id: 12345679
},
}
]
}
]
return(
<Tree data = {treeData}/>
)
So now I have an external JSON file that looks like this:
{
"name": "Parent",
"attributes": {
"id": 12345678,
},
"children": [
{
"name": "Child",
"attributes": {
"id": 12345679,
}
}
]
}
]
And in my program:
const[treeData, setTreeData] = useState(undefined)
const jsonSource = '../../data/tree-data.json'
/* Used to center the tree on render */
useEffect(()=> {
// Some irrelevant code here...
// Parse JSON data (Relevant)
return function parseJSONFile(){
treeUtil.parseJSON(jsonSource).
then((data) => setTreeData({data}))
.catch((error)=>console.log(error))
}
}, []);
return(
<Tree data = {treeData} />
)
Aand it does not work. Error message:
TypeError: Cannot set property 'id' of undefined
Which makes me confused because it's my first time dealing with JSON. Any insight on this can help!
You can do it in a very simple way (No need for extra works you are doing):
import treeData from "./tree-data.json"; // Import your json file (Path to your json file)
function App() {
return <Tree data={treeData} />;
}
Your code have many problems including you write your parseJSONFile in return of useEffect, Also you just defined parseJSONFile and you are not calling it, and even you call, it will be executed on component unmount (because you call it on return of useEffect), Also initial state of your treeData is undefined and it's the cause of TypeError: Cannot set property 'id' of undefined.
My codesandbox link (https://codesandbox.io/s/react-hooks-counter-demo-dh0q4?file=/src/index.js).
*** Problem solved : json.stringify was the problem.. much easier to handle when its gone.
var DBName = result['Document']['SW.Blocks.GlobalDB']['AttributeList']['Name'];
I have a xml file which describes a datablock from a PLC and want to get specific values with JS.
I converted it with xml2js module, so i have a json object to work with.
{
"Document": {
"Engineering": {
"$": {
"version": "V15"
}
},
"SW.Blocks.GlobalDB": {
"$": {
"ID": "0"
},
"HeaderAuthor": "",
"HeaderFamily": "",
"HeaderName": "",
"HeaderVersion": "0.1",
"Interface": {
...
...
"Name": "datentypen",
"Number": "6",
"ParameterModified": {
"_": "2018-09-05T11:49:37.0862092Z",
"$": {
"ReadOnly": "true"
}
},
}
}
I want to print out the "Name" and the "Number", which are part of the "AttributeList".
So how to handle with the "SW.Blocks.GlobalDB"?
Getting error : "TypeError: Cannot read property 'SW' of undefined"
var fs = require('fs');
var xml2js = require('xml2js');
var xml = fs.readFileSync('datentypen.xml');
var parser = new xml2js.Parser({explicitArray: false});
parser.parseString(xml, function(err, result) {
if (err) {
console.error('xml2js.parse error: ',err);
} else {
var injson = JSON.stringify(result,null,3);
console.log(injson);
// var injson2 = JSON.parse(injson);
// var DBnummer = injson.Document.SW.Blocks.GlobalDB.AttributeList["Name","Number"];
// console.log(DBNummer);
};
});
I read a lot about this theme but didnt found a concrete answer..
When i write ["SW.Blocks.GlobalDB"], an error about [ comes around.
Can you try reading the JSON array using Key-Value pair? I had similar issues but with a different programming language.
Im building a React app and I have a quite complex JSON file where I need to find and output certain values of an object in an array.
Im trying to output all my people from my JSON, they look something like this:
people: [
{
"id": 1,
"email": "Sincere#april.biz",
"address": [
{
"street": "Kulas Light",
"type": "house",
"attribute": {
"sketch": "sketch.jpg",
"photo": "photo.jpg"
}
},
{
"street": "Lorem Ipsum",
"type": "apartment",
"attribute": {
"sketch": "sketch.jpg",
"photo": "photo.jpg"
}
}
]
}
]
I have no problem to output the email, doing it like so:
var App = React.createClass({
getInitialState: function () {
return {
results: {}
}
},
componentDidMount() {
fetch(REQUEST_URL) // fetch from API, returns JSON
.then(res => res.json())
.then(data => {this.setState(
{ results: data.people}
);
})
},
renderResult : function(key){
return <Result key={key} index={key} details={this.state.results[key]}/>
},
render : function() {
return (
<ul>
{Object.keys(this.state.results).map(this.renderResult)}
</ul>
)
}
});
var Result = React.createClass({
render : function() {
return (
<li>
{this.props.details.email}
<img src="{this.props.details.address.type=house.attribute.photo}"/>
</li>
)
}
});
ReactDOM.render(App, document.querySelector('#app'));
However, now I need to output "photo" but only for "type": "house". I tried this but no luck, well aware that this is way off. Im quite new to handling JSON data and React and Google hasn't helped me even after a few hours of trying to solve this.
The .address property isn't an object but an array of objects so
.type is not available directly on .address:
this.state.results.people.address.type
// .type property doesn't exist on array
Solution:
You can use Array.prototype.filter on .address to obtain an array of objects that have a property type whose value is "house":
var houseAddresses = this.state.results.people.address.filter(function(value){
return value.type === "house";
});
Here, houseAddress will be an array of objects whose type value is 'house".
You can then loop through the array to create the relevant JSX using for, Array#forEach or Array#map. The following example uses Array#map:
const houseImgTags = houseAddresses.map(function(house, index){
return (
<img
src={house.attribute.photo}
key={'house'+index}
/>
);
});
(A key was added here in case there are more than one instance of a house object)
You can simply write.
<img src={this.states.results.address.type==="house"?house.attribute.photo : otherwise_photo}/>
Basically this would compare address.type is house or not,then return the result corresponded.
I would like to merge my json files with a gulp task with a prefix per jsonfile.
I have several json files like this
{
"en": {
"title": "Component title english",
"subtitle": "Component subtitle english",
},
"nl": {
"title": "Component title dutch",
"subtitle": "Component subtitle dutch",
}
}
I would like to merge these with the component name as a prefix so the outcome of the merge will be come something like this:
"componentBlogBox": {
"en": {
"title": "Component title english",
"subtitle": "Component subtitle english",
},
"nl": {
"title": "Component title dutch",
"subtitle": "Component subtitle dutch",
}
}
},
"componentCaseSlider": {
"en": {
"title": "Caseslider title english",
"subtitle": "caseslider subtitle english",
},
"nl": {
"title": "Component title dutch",
"subtitle": "Component subtitle dutch",
}
}
I have this gulp task using node module gulp-merge-json but this just replaces the keys to form one.
gulp.task('json-merge', function(){
gulp.src(['dest/json/blog-box/home.json', 'dest/json/case-slider/home.json'])
.pipe(jsonMerge('combined.json'))
.pipe(gulp.dest('dest/json'));
});
Is there a way to merge using a gulptask and without editing all my json files?
gulp-merge-json offers an edit option that allows you to modify the parsed JSON for each file before all of them are merged.
So in your case all you have to do is stick the parsed JSON for each file into a new object like {'componentBlogBox': parsedJson} and return that. Whether the property should be componentBlogBox or componentCaseSlider you can determine by looking at the path of the file:
var jsonMerge = require('gulp-merge-json');
var path = require('path');
function camelCase(str) {
return str.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
}
gulp.task('json-merge', function(){
return gulp.src([
'dest/json/blog-box/home.json',
'dest/json/case-slider/home.json'
])
.pipe(jsonMerge({
fileName: 'combined.json',
edit: function(parsedJson, file) {
var component = path.basename(path.dirname(file.path));
var editedJson = {};
editedJson[camelCase('component-' + component)] = parsedJson;
return editedJson;
}
}))
.pipe(gulp.dest('dest/json'));
});
(Credit for the camelCase function goes to this answer.)
Awesome Sven, just what I was looking for. The camelcase thing wasn't really an must-have but it's a nice touch for further development.
I simplified it into this
gulp.task('json-merge', function(){
return gulp.src([
'dest/json/blog-box/home.json',
'dest/json/case-slider/home.json'
])
.pipe(jsonMerge({
fileName: 'combined.json',
edit: function(parsedJson, file) {
var component = path.basename(path.dirname(file.path));
var editedJson = {};
editedJson[component] = parsedJson;
return editedJson;
}
}))
.pipe(gulp.dest('dest/json'));
});