json 使用 Angular 4.3 HttpClient 解析日期
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/46559268/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Parse date with Angular 4.3 HttpClient
提问by Ralf Schneider
I'm currently switching to the new HttpClient of Angular 4.3. One advantage is that I can specify a type info on the GET method and that the returned JSON is parsed into the given type, e.g.
我目前正在切换到 Angular 4.3 的新 HttpClient。一个优点是我可以在 GET 方法上指定类型信息,并将返回的 JSON 解析为给定的类型,例如
this.http.get<Person> (url).subscribe(...)
But unfortunately, all dates in the JSON are parsed as numbers in the resulting object (probably because the Java Date objects are serialized as numbers in the backend).
但不幸的是,JSON 中的所有日期在结果对象中都被解析为数字(可能是因为 Java 日期对象在后端被序列化为数字)。
With the old Http I used a reviver function when calling JSON.parse() like this:
使用旧的 Http 我在调用 JSON.parse() 时使用了一个 reviver 函数,如下所示:
this.http.get(url)
.map(response => JSON.parse(response.text(), this.reviver))
and in the reviver function I created date objects from the numbers:
在 reviver 函数中,我从数字创建了日期对象:
reviver (key, value): any {
if (value !== null && (key === 'created' || key === 'modified'))
return new Date(value);
return value;
}
Is there a similar mechanism with the new HttpClient? Or what is the best practice to do conversion when the JSON is parsed?
新的 HttpClient 是否有类似的机制?或者在解析 JSON 时进行转换的最佳实践是什么?
回答by bygrace
Unfortunately there doesn't seem to be a way to pass a reviver to the JSON.parse method that is used within the Angular HttpClient. Here is the source code for where they call JSON.parse: https://github.com/angular/angular/blob/20e1cc049fa632e88dfeb00476455a41b7f42338/packages/common/http/src/xhr.ts#L189
不幸的是,似乎没有办法将 reviver 传递给 Angular HttpClient 中使用的 JSON.parse 方法。这是他们调用 JSON.parse 的源代码:https: //github.com/angular/angular/blob/20e1cc049fa632e88dfeb00476455a41b7f42338/packages/common/http/src/xhr.ts#L189
Angular will only parse the response if the response type is set to "json" (you can see this on line 183). If you don't specify the response type then Angular defaults to "json" (https://github.com/angular/angular/blob/20e1cc049fa632e88dfeb00476455a41b7f42338/packages/common/http/src/request.ts#L112).
如果响应类型设置为“json”(你可以在第 183 行看到),Angular 只会解析响应。如果您未指定响应类型,则 Angular 默认为“json”(https://github.com/angular/angular/blob/20e1cc049fa632e88dfeb00476455a41b7f42338/packages/common/http/src/request.ts#L112)。
So you could use a response type of "text" and parse the json yourself. Or you could just be lazy and serialize and then deserialize the response (JSON.parse(JSON.stringify(res.body), reviver)).
因此,您可以使用“文本”响应类型并自己解析 json。或者你可以只是懒惰并序列化然后反序列化响应(JSON.parse(JSON.stringify(res.body), reviver))。
Rather than modifying every call you could create an interceptor like the following:
您可以创建一个拦截器,而不是修改每个调用,如下所示:
json-interceptor.ts
json-interceptor.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);
}
}
Then you have to provide it in your module:
然后你必须在你的模块中提供它:
*.module.ts
*.module.ts
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { JsonInterceptor } from '...';
@NgModule({
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: JsonInterceptor,
multi: true
}
]
})
...
In the example I tried to mimic how angular was doing the parsing as much as possible. They don't seem to export HttpJsonParseError so I couldn't cast the error to that type. It probably isn't perfect but I hope it gets the idea across.
在这个例子中,我试图尽可能地模仿 angular 是如何进行解析的。他们似乎没有导出 HttpJsonParseError 所以我无法将错误转换为该类型。它可能并不完美,但我希望它能传达这个想法。
Here is a running example (look in the console to see the date parsed): https://stackblitz.com/edit/json-interceptor
这是一个正在运行的示例(在控制台中查看解析的日期):https: //stackblitz.com/edit/json-interceptor
I created a feature request here: https://github.com/angular/angular/issues/21079
我在这里创建了一个功能请求:https: //github.com/angular/angular/issues/21079
回答by Lerner
This works for me:
这对我有用:
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);
}
}
}
}
The advantage of this over the answer of bygraceis that you don't need to parse to json yourself, you just convert the dates after angular is done with the parsing.
与bygrace的答案相比,这样做的优势在于您不需要自己解析为 json,您只需在 angular 解析完成后转换日期即可。
This also works with arrays and nested objects. I modified thisit should support arrays.
这也适用于数组和嵌套对象。我修改了它应该支持数组。
回答by Markus Pscheidt
Similar to Jonas Stensved's answer, but using pipes:
类似于Jonas Stensved 的回答,但使用管道:
import { map } from "rxjs/operators";
this.http.get(url)
.pipe(
map(response => {
response.mydate = new Date(response.mydate);
return response;
})
Note the different import syntax of the mapoperator.
请注意map运算符的不同导入语法。
Pipes were introduced in RxJS 5.5. They facilitate import handling, code readability, and reduce bundle size. See Understanding Operator Imports.
管道是在 RxJS 5.5 中引入的。它们有助于导入处理、代码可读性并减少包大小。请参阅了解运算符导入。
回答by Jonas Stensved
You still can, but you need to import the map()-operator from rxjs like this:
你仍然可以,但你需要map()像这样从 rxjs导入-operator:
import 'rxjs/add/operator/map';
Then you can, just as Diego pointed out, use the maplike this:
然后你可以,就像迭戈指出的那样,使用map这样的:
return this.http.get<BlogPost>(url)
.map(x => {
x.published = new Date(String(x.published));
return x;
})
[...]
回答by Diego Maninetti
You can use:
您可以使用:
this.http.get(url, { responseType: 'text' })
.map(r => JSON.parse(r, this.reviver))
.subscribe(...)
Update for rxjs 6+
rxjs 6+ 的更新
this.http.get(url, { responseType: 'text' })
.pipe(map(r => JSON.parse(r, this.reviver)))
.subscribe(...)

![json Angular:'找不到'object'类型的不同支持对象'[object Object]'。NgFor 仅支持绑定到 Iterables,例如 Arrays'](/res/img/loading.gif)