Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom JSON reviver #9

Closed
gustavopch opened this issue Feb 3, 2019 · 7 comments
Closed

Custom JSON reviver #9

gustavopch opened this issue Feb 3, 2019 · 7 comments

Comments

@gustavopch
Copy link

What do you think about letting the user determine a custom JSON reviver? Would be pretty useful for reviving Date objects.

@lukeed
Copy link
Owner

lukeed commented Feb 3, 2019

Hey, what would this look like?

Initially I'm a bit hesitant as I think you could/would just tidy up the already-parsed item(s) you get in the data key.

@gustavopch
Copy link
Author

It would be passed through the opts.

Here are hypothetical scenarios for getting orders from an API:

Scenario 1: Clean 😃

import { get } from 'httpie'
import { reviver } from './utils'

const orders = await get('/orders', { reviver })

Scenario 2: Extra lib 😕

Also this is probably worse in performance than just using a reviver function during JSON.parse.

import { get } from 'httpie'
import { traverse } from 'some-traverse-lib-that-wont-be-used-for-anything-else'
import { isDateString } from './utils'

let orders = await get('/orders')

orders = traverse((key, value) => isDateString(value) ? new Date(value) : value)

Scenario 3: Just ugly 😩

Could get a lot uglier depending on the complexity of the schema (e.g. optional props would require extra checking to prevent cannot read X from undefined).

import { get } from 'httpie'

let orders = await get('/orders')

orders = orders.map(order => ({
  ...order,
  createdAt: new Date(order.createdAt),
  updatedAt: new Date(order.updatedAt),
  deliveryTime: new Date(order.deliveryTime),
  items: order.items.map(item => ({
    ...item,
    product: {
      ...item.product,
      createdAt: new Date(item.product.createdAt),
      updatedAt: new Date(item.product.createdAt),
      category: {
        ...item.product.category,
        createdAt: new Date(item.product.category.createdAt),
        updatedAt: new Date(item.product.category.createdAt),
      }
    }
  }))
}))

Is there a better option to deal with this that I'm forgeting?

@lukeed
Copy link
Owner

lukeed commented Feb 4, 2019

Hey, thanks for the examples :D

I'm still a little bit confused tho – Option 3 is what you'll be doing in Option 1 anyway, and it's also what your 'some-traverse-lib-...-else' module will be doing too. Your sample just isn't recursive, that's why it "looks ugly"

import { get } from 'httpie';
import { isDateString } from './utils';

let { data } = await get('/orders');

const orders = traverse(data, val => {
  // do whatever you want, return new or old values
  return isDateString(val) ? new Date(val) : val;
});

// ---

// recursive traverse helper
function traverse(any, transformer) {
  if (any == null) return;
  if (Array.isArray(any)) {
  	return any.map(x => traverse(x, transformer));
  } else if (typeof any === 'object') {
  	for (let k in any) {
  		any[k] = traverse(any[k], transformer);
  	}
  	return any;
  } else {
  	return transformer(any);
  }
}

@gustavopch
Copy link
Author

Option 3 is basically doing it manually.

Option 2 may use an external lib or an internal helper as you showed.

My question is: why would we prefer to write the traverse helper (or use an external lib) given that JSON.parse already gives us the possibility of using a reviver function?

@lukeed
Copy link
Owner

lukeed commented Feb 4, 2019

Oh! I completely forgot JSON.parse has an optional reviver argument!

Yes, we can do this. 👍 I didn't want to include a bunch of data-handling stuff to httpie, but I have no problems exposing what's already there 😃

@lukeed
Copy link
Owner

lukeed commented Feb 4, 2019

Will get this in momentarily, battling the darn types.

@lukeed
Copy link
Owner

lukeed commented Feb 4, 2019

Now available in 1.1.0 – thanks!

@lukeed lukeed closed this as completed Feb 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants