Json 使用Angular 4.3 HttpClient解析日期

Json 使用Angular 4.3 HttpClient解析日期,json,angular,date,angular-httpclient,Json,Angular,Date,Angular Httpclient,我目前正在切换到Angular 4.3的新HttpClient。一个优点是我可以在GET方法上指定一个类型信息,并且返回的JSON被解析为给定的类型,例如 this.http.get<Person> (url).subscribe(...) 在reviver函数中,我根据数字创建了日期对象: reviver (key, value): any { if (value !== null && (key === 'created' || key === 'modif

我目前正在切换到Angular 4.3的新HttpClient。一个优点是我可以在GET方法上指定一个类型信息,并且返回的JSON被解析为给定的类型,例如

this.http.get<Person> (url).subscribe(...)
在reviver函数中,我根据数字创建了日期对象:

reviver (key, value): any {
  if (value !== null && (key === 'created' || key === 'modified'))
    return new Date(value);

  return value;
}
新的HttpClient有类似的机制吗?或者,解析JSON时进行转换的最佳实践是什么?

您可以使用:

this.http.get(url, { responseType: 'text' })
    .map(r => JSON.parse(r, this.reviver))
    .subscribe(...)
rxjs 6+的更新

this.http.get(url, { responseType: 'text' })
    .pipe(map(r => JSON.parse(r, this.reviver)))
    .subscribe(...)

不幸的是,似乎没有办法将恢复程序传递给HttpClient中使用的JSON.parse方法。下面是他们调用JSON.parse的源代码:

Angular将只在响应类型设置为“json”时解析响应(您可以在第183行看到这一点)。如果不指定响应类型,则默认为“json”()

因此,您可以使用响应类型“text”并自己解析json。或者,您可以只是懒惰地序列化然后反序列化响应(
JSON.parse(JSON.stringify(res.body),reviver)

您可以创建如下拦截器,而不是修改每个调用:

json拦截器.ts

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map';

// https://github.com/angular/angular/blob/master/packages/common/http/src/xhr.ts#L18
const XSSI_PREFIX = /^\)\]\}',?\n/;

/**
 * Provide custom json parsing capabilities for api requests.
 * @export
 * @class JsonInterceptor
 */
@Injectable()
export class JsonInterceptor implements HttpInterceptor {

  /**
   * Custom http request interceptor
   * @public
   * @param {HttpRequest<any>} req
   * @param {HttpHandler} next
   * @returns {Observable<HttpEvent<any>>}
   * @memberof JsonInterceptor
   */
  public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (req.responseType !== 'json') {
      return next.handle(req);
    }
    // convert to responseType of text to skip angular parsing
    req = req.clone({
      responseType: 'text'
    });

    return next.handle(req).map(event => {
      // Pass through everything except for the final response.
      if (!(event instanceof HttpResponse)) {
        return event;
      }
      return this.processJsonResponse(event);
    });
  }

  /**
   * Parse the json body using custom revivers.
   * @private
   * @param {HttpResponse<string>} res
   * @returns{HttpResponse<any>}
   * @memberof JsonInterceptor
   */
  private processJsonResponse(res: HttpResponse<string>): HttpResponse<any> {
      let body = res.body;
      if (typeof body === 'string') {
        const originalBody = body;
        body = body.replace(XSSI_PREFIX, '');
        try {
          body = body !== '' ? JSON.parse(body, (key: any, value: any) => this.reviveUtcDate(key, value)) : null;
        } catch (error) {
          // match https://github.com/angular/angular/blob/master/packages/common/http/src/xhr.ts#L221
          throw new HttpErrorResponse({
            error: { error, text: originalBody },
            headers: res.headers,
            status: res.status,
            statusText: res.statusText,
            url: res.url || undefined,
          });
        }
      }
      return res.clone({ body });
  }

  /**
   * Detect a date string and convert it to a date object.
   * @private
   * @param {*} key json property key.
   * @param {*} value json property value.
   * @returns {*} original value or the parsed date.
   * @memberof JsonInterceptor
   */
  private reviveUtcDate(key: any, value: any): any {
      if (typeof value !== 'string') {
          return value;
      }
      if (value === '0001-01-01T00:00:00') {
          return null;
      }
      const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
      if (!match) {
          return value;
      }
      return new Date(value);
  }
}
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { JsonInterceptor } from '...';

@NgModule({
    providers: [
        {
            provide: HTTP_INTERCEPTORS,
            useClass: JsonInterceptor,
            multi: true
        }
    ]
})
...
在这个例子中,我试图尽可能多地模仿angular在进行解析。它们似乎没有导出HttpJsonParseError,因此我无法将错误转换为该类型。它可能并不完美,但我希望它能让人明白这一点

下面是一个正在运行的示例(查看控制台以查看解析的日期):

我在这里创建了一个功能请求:

您仍然可以,但是您需要从rxjs导入
map()
-操作符,如下所示:

this.http.get(url)
  .map(response => JSON.parse(response.text(), this.reviver))
import 'rxjs/add/operator/map';
return this.http.get<BlogPost>(url)
.map(x => {
x.published = new Date(String(x.published));
    return x;
})
[...]
然后,正如迭戈指出的那样,你可以像这样使用
地图

this.http.get(url)
  .map(response => JSON.parse(response.text(), this.reviver))
import 'rxjs/add/operator/map';
return this.http.get<BlogPost>(url)
.map(x => {
x.published = new Date(String(x.published));
    return x;
})
[...]
返回此.http.get(url)
.map(x=>{
x、 发布=新日期(字符串(x.published));
返回x;
})
[...]
这对我很有用:

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class ApiInterceptor implements HttpInterceptor {
  private dateRegex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)$/;

  private utcDateRegex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/;

  constructor() { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
      .do((event: HttpEvent<any>) => {
        if (event instanceof HttpResponse) {
          this.convertDates(event.body);
        }
      });
  }

  private convertDates(object: Object) {
    if (!object || !(object instanceof Object)) {
      return;
    }

    if (object instanceof Array) {
      for (const item of object) {
        this.convertDates(item);
      }
    }

    for (const key of Object.keys(object)) {
      const value = object[key];

      if (value instanceof Array) {
        for (const item of value) {
          this.convertDates(item);
        }
      }

      if (value instanceof Object) {
        this.convertDates(value);
      }

      if (typeof value === 'string' && this.dateRegex.test(value)) {
        object[key] = new Date(value);
      }
    }
  }
}
从'@angular/core'导入{Injectable};
从'@angular/common/http'导入{HttpInterceptor,HttpRequest,HttpHandler,HttpEvent,HttpErrorResponse,HttpResponse};
从“rxjs/Observable”导入{Observable};
@可注射()
导出类ApiInterceptor实现HttpInterceptor{
私有dateRegex=/^(\d{4})-(d{2})-(d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)$/;
私有utcDateRegex=/^(\d{4})-(d{2})-(d{2})T(\d{2}):(\d{2}):(\d{2}:(\d{2}:(\.\d*)?)Z$/;
构造函数(){}
intercept(请求:HttpRequest是指您不需要自己解析为json,只需在解析完成后转换日期即可

这也适用于数组和嵌套对象。 我修改了它,它应该支持阵列。

类似于,但使用管道:

import { map } from "rxjs/operators";

this.http.get(url)
  .pipe(
    map(response => {
      response.mydate = new Date(response.mydate);
      return response;
    })
请注意
map
运算符的不同导入语法


管道是在RxJS 5.5中引入的。它们有助于导入处理、代码可读性和减少捆绑包大小。请参阅。

是否可以将此恢复器函数重新定位到您期望的对象,即Person?Person很可能是一个接口,因此它没有方法。我不相信没有更好的方法用d实现JSON APIate/times.Up尽我所能投票。这可能有助于某人知道此拦截器需要注册才能工作。此链接帮助我这样做:我还发现,注册此拦截器后,我的全局错误处理程序停止显示来自有效负载的错误消息,因为错误响应现在未被解析。要解决此问题,我需要o修改
intercept
方法的结尾,就像这样
return next.handle(req).catch((e,c)=>{e.error=JSON.parse(e.error);return Observable.throw(e);}).map(事件=>{…
这只是一个提示,显然可以改进,至少可以恢复日期。@evilkos很好。我添加了一个注册拦截器的示例。@evilkos你是说HttpClient正在将错误解析为json吗?我在他们的OneError中没有看到任何可以这样做的内容。负载可能是无效的json。所以你可能需要在全局错误处理程序中处理此问题。我添加了一个没有“T”的新模式,它对我有效:)我得到错误
属性“map”在类型“Observable”上不存在。
由于rxjs的新版本,您必须使用
管道()
操作符。