5

I'm looking for the most effective way to replace a JSON Object in a file.

20150628 - Update at the bottom of this post

Here's the scenario:

I have a bunch of JSON files (many) and in these files are large chunks of JSON (sometimes 20-30K lines of it). These are configurations for various testing routines we have. Recently, a change was required to change an object from this:

"createdAt": {
    "year": 2014,
    "month": 11,
    "dayOfMonth": 24,
    "hourOfDay": 2,
    "minute": 22,
    "second": 54
}

to a format like this:

"createdAt":"2015-05-12T21:14:51Z"

Let's even make this easier. I want to replace all of the createdAt and updatedAt fields in my JSON object (which there can be many) with:

   "createdAt":"2015-05-12T21:14:51Z"

or

   "updatedAt":"2015-05-12T21:14:51Z"

There are NUMEROUS (100's of these) objects in each file, with different values for the fields. I need to go through and replace every createdAt and updatedAt object with the new format. The date's do not matter. I can have them be anything.

I can do this by hand, but it will literally take me a day or two to do of full time work (I know, I tried to do one file and after 1/2 hour I gave up, it was taking way too long).

How can I do this programmatically?

Regex? Sed? Something else?

Final note: I only need to do this once. After that, I won't need to do it again.

Thanks for any tips!

Example JSON: (Just imagine the real one is 30,000 lines!) :)

{ "products": [
          {
            "displayOrder": 3,
            "product": {
              "foo": "bar",
              "createdAt": {
                "year": 2014,
                "month": 11,
                "dayOfMonth": 24,
                "hourOfDay": 2,
                "minute": 22,
                "second": 54
              },
              "description": "Fizz Bin",
              "id": "8765309",
              "modelNumber": "call-it",
              "name": "Boom",
              "price": {
                "amount": 100,
                "currency": "USD"
              },
              "type": "Active",
              "updatedAt": {
                "year": 2015,
                "month": 1,
                "dayOfMonth": 27,
                "hourOfDay": 19,
                "minute": 33,
                "second": 25
              }
            }
          },
          {
            "displayOrder": 4,
            "product": {
              "foo": "barx",
              "createdAt": {
                "year": 2013,
                "month": 1,
                "dayOfMonth": 4,
                "hourOfDay": 3,
                "minute": 2,
                "second": 5
              },
              "description": "Fizzy Stuff",
              "id": "876511111",
              "modelNumber": "zoom-zoom-1000",
              "name": "Zoom Zoom 1000",
              "price": {
                "amount": 1000,
                "currency": "USD"
              },
              "type": "Active",
              "updatedAt": {
                "year": 2011,
                "month": 5,
                "dayOfMonth": 25,
                "hourOfDay": 15,
                "minute": 35,
                "second": 55
              }
            }
          }
        ]
}

UPDATE 20150628

For those wondering, here's the gulpfile I wrote to accomplish exactly what I wanted. It is based off of the accepted answer. It will recursively search the tree for what I'm looking for an replace it when found. Its not the prettiest thing in the world, but it did exactly what I needed and saved me a couple weeks of manual time. Total time to process all my files? Under 100ms. Amazing.

var gulp = require('gulp');
var change = require('gulp-change');

function searchTreeForDates(obj) {
    if(typeof(obj) === 'object') {
        for (var key in obj) {
            if (typeof(obj[key]) === 'object' && (key === 'createdAt' || key === 'updatedAt')) {
                obj[key] = "2015-06-29T00:53:00Z";
            } else {
                obj[key] = searchTreeForDates(obj[key])
            }
        }
    }
    return obj; 
}

function updateDate(content) {
    var obj = JSON.parse(content);
    obj = searchTreeForDates(obj);
    return JSON.stringify(obj);
}

gulp.task('change', function() {
    return gulp.src('*.json')
        .pipe(change(updateDate))
        .pipe(gulp.dest('changed/'))
});
Donn Felker
  • 9,553
  • 7
  • 48
  • 66
  • If it was me I'd write a node script to read it as JSON and then write it back as JSON. Basically convert the date object to a a string and then save it back. I dont want to write the code for you but its at least a starting point. – Nix May 21 '15 at 21:07
  • I wouldn't go for manual textfile parsing - what programming languages/environments are you familiar with? You can do this in pretty much any language that can read and write JSON - it would be reading and deserializing the existing JSON, transforming the applicable fields as you need, then serializing and rewriting the file. – Joe Enos May 21 '15 at 21:08
  • 1
    @JoeEnos great minds think a like ;) – Nix May 21 '15 at 21:08
  • I'd write a Node.js script that uses streams for file i/o. Use `fs` streams for reading the data and writing the result, and a simple transform stream of your own in between. – joews May 21 '15 at 21:08
  • Now that I think of it you could even use gulpjs to do it. (and now I'm interested in it) – Nix May 21 '15 at 21:09
  • I know a lot of languages. I already started writing a node script and about 1/2 the way through I began to wonder "am I doing this the best way" and decided to ask here. I can do this in Node/JS, Java, Ruby, Python. Looks like best method may be to just script it, I **was** on the right path. :) – Donn Felker May 21 '15 at 21:13
  • JSON.parse() includes a reviver callback the visits every value in a deep JSON tree as it's parsed. you can convert objects, in root or deep in the tree from the reviver callback. – dandavis May 21 '15 at 21:15

3 Answers3

2

Here is an initial stab. You implement your own "date parsing logic." It requires you to install gulp. And save this in a gulpfile.js . You would need to possibly loop over all the properties that are "date" objects. But that logic isn't that hard.

var gulp = require('gulp');
var change = require('change');

function translateDate(dateField){
    return dateField.A + dateField.b + ...;

}
function updateDate(content) {
    var obj = JSON.parse(content);
    //loop over the obj properties and call the below
    // for the ones you want to change.
    obj.dateField = translateDate(obj.dateField);
    return JSON.stringify(obj);
}

gulp.task('change', function() {
    return gulp.src('**/*.json')
      .pipe(change(updateDate))
      .pipe(gulp.dest('changed/'))
});
Nix
  • 57,072
  • 29
  • 149
  • 198
  • I have gulp installed and node/etc. This looks similar to what I was doing, only much more elegant! :) Thanks! I'll use this as my new jump off. I'm glad I was on the right path then. Thanks @nix – Donn Felker May 21 '15 at 21:18
  • Let me know if you have any questions. – Nix May 21 '15 at 21:19
  • Used your method above as inspiration and wrote exactly what I needed. Thanks for the tips. – Donn Felker Jun 29 '15 at 01:25
0

Why not manually?

   function formatDate(dateObject){
       var formattedDate = 
                  dateObject['year']+'-'+
                  dateObject['month']+'-'+ 
                  dateObject['dayOfMonth']+'T'+
                  dateObject['hourOfDay']+':'+
                  dateObject['minute']+':'+
                  dateObject['second']+'Z';
    }
    var jsonArray = {...};

    for(var key in jsonArray){
        for(var i = 0; i < jsonArray[key].length; i++){
            jsonArray[key][i]['createdAt'] = formatDate(jsonArray[key]['createdAt']); 
            jsonArray[key][i]['updatedAt'] = formatDate(jsonArray[key]['updatedAt']); 

    }
}
AmmarCSE
  • 30,079
  • 5
  • 45
  • 53
  • Problem is, these createdAt and updatedAt all EVERYWHere in the json file. The example above is very simplified. I basically need to walk the entire JSON tree. Not hard, just wondering if I was approaching it incorrectly. – Donn Felker May 21 '15 at 21:14
  • do watch the timezone-less date construction when outputting "Z" on a final timestamp... – dandavis May 21 '15 at 21:14
  • I'm just going to take a known value that we have and put it into those fields for now. Thanks for the heads up though! – Donn Felker May 21 '15 at 21:17
0

Open each file, change the property with a convert function and then save the new JSON:

function changeDate(obj) {
  var newObject = obj.year + '-' + obj.month + '-' + obj.dayOfMonth + 'T' + obj.hourOfDay + ':' + obj.minute + ':' + obj.second;
  return newObject;
}

// here you open the file and stores it's content in the products variable.
for (var i = 0; i < products.length; i++) {
  var product = products[i];
  product.product.createdAt = changeDate(product.product.createdAt);
  product.product.updatedAt = changeDate(product.product.updatedAt);
}
// .. now you need to save the modified json
jparaya
  • 1,331
  • 11
  • 15