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

feat(common): support custom JSON parser in HttpClient #25027

Closed
wants to merge 1 commit into from

Conversation

trotyl
Copy link
Contributor

@trotyl trotyl commented Jul 22, 2018

closes #21079

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

[ ] Bugfix
[x] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Documentation content changes
[ ] angular.io application / infrastructure changes
[ ] Other... Please describe:

What is the current behavior?

Issue Number: #21079

When user works with an extended JSON format, eg. JSON5, due to it's not compatible with JSON.parse(), user have to make it raw text and parse it manually.

But JSON5 format is still conceptually 'json' rather than 'text', it's a fully superset of ES JSON, just being more human friendly.

What is the new behavior?

Simply with:

@NgModule({
  providers: [
    { provider: JsonParser, useValue: JSON5 }
  ]
})
class AppModule {}

Or strip a different receiver:

class MyJsonParser implements JsonParser {
  parse(text: string, reviver?: (key: any, value: any) => any): any {
    const strippedText = text.trimStart(CUSTOM_XSSI)
    return JSON.parse(strippedText, reviver)
  }
}

Or have a different reviver:

class MyJsonParser implements JsonParser {
  parse(text: string, reviver?: (key: any, value: any) => any): any {
    const wrappedReviver = wrapReviver(reviver)
    return JSON.parse(text, wrappedReviver)
  }
}

Then server could provide JSON5 response directly and no error would be raised.

Does this PR introduce a breaking change?

[ ] Yes
[x] No

Other information

@trotyl trotyl force-pushed the json-custom-parse branch 2 times, most recently from 6ed46ea to 18ad2a9 Compare July 22, 2018 13:51
@vicb vicb added area: common Issues related to APIs in the @angular/common package area: common/http and removed area: common/http labels Jul 23, 2018
@pkozlowski-opensource pkozlowski-opensource added area: common/http and removed area: common Issues related to APIs in the @angular/common package labels Dec 19, 2018
@ngbot ngbot bot added this to the needsTriage milestone Dec 19, 2018
@Lonli-Lokli
Copy link

What should be done to accept this PR?

Copy link
Member

@petebacondarwin petebacondarwin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@trotyl thanks for this PR. I am sorry it slipped under our radar for so long. I rebased it and cleaned up the conflicts. But I have a few suggestions for how to make it a bit slimmer.

@@ -70,7 +77,8 @@ interface PartialResponse {
*/
@Injectable()
export class HttpXhrBackend implements HttpBackend {
constructor(private xhrFactory: XhrFactory) {}
constructor(private xhrFactory: XhrFactory, @Inject(JSON_PARSER) private jsonParser: JsonParser) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about making this optional, then we wouldn't have to define a provider for the default case:

Suggested change
constructor(private xhrFactory: XhrFactory, @Inject(JSON_PARSER) private jsonParser: JsonParser) {
constructor(private xhrFactory: XhrFactory, @Optional() @Inject(JSON_PARSER) private jsonParser?: JsonParser) {
if (jsonParser == null) { // note == matches both `null` and `undefined`
this.jsonParser = JSON;
}

@@ -33,7 +33,7 @@ const XSSI_PREFIX = ')]}\'\n';
let backend: HttpXhrBackend = null!;
beforeEach(() => {
factory = new MockXhrFactory();
backend = new HttpXhrBackend(factory);
backend = new HttpXhrBackend(factory, JSON);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If jsonParser was optional this would not need to change.

@@ -16,6 +16,13 @@ import {HttpDownloadProgressEvent, HttpErrorResponse, HttpEvent, HttpEventType,

const XSSI_PREFIX = /^\)\]\}',?\n/;

/**
* Utility for parsing JSON text to JavaScript object
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would need a bit more documentation for this and the JSON_PARSER token below, ideally with an example and referencing each other.

Comment on lines +134 to +136
export function _JSON(): JsonParser {
return JSON;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If jsonParser was optional then this would not be needed.

@mhevery mhevery added this to Needs review in mhevery-review-queue via automation Jan 28, 2021
@mhevery mhevery moved this from Needs review to Discuss in mhevery-review-queue Jan 28, 2021
@petebacondarwin
Copy link
Member

We discussed this in the team meeting yesterday. The general consensus is that this is actually fairly straightforward to do via an interceptor and that the real problem is the lack of discoverability of this approach to solving the problem.

We accept that the problem is compounded by the fact that HttpClient defaults to json parsing with its own parser and so the natural solution that people lean towards is replacing this built-in JSON parsing directly. It would have been better if the default had been text so that JSON parsing felt more like a special case that would lead more naturally to overriding via an interceptor.

The proposal then, is not to merge this feature into core, but instead to improve the documentation to lead developers clearly to an interceptor based solution.

Here is a link to an example of how custom JSON parseing could be done in a reusable interceptor: https://stackblitz.com/edit/angular-ivy-nrdj5q?file=src%2Fapp%2Fapp.module.ts. Note that this could be wrapped up and distributed as a 3rd party library and then the application developer would only need to provide their own JsonParser class.

mhevery-review-queue automation moved this from Discuss to Done Jan 29, 2021
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Mar 1, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

Ability to configure custom JSON.parse reviver for the HttpClient
8 participants