typescript 从JSON以Angular 指定日期/时间

p5fdfcr1  于 2022-11-26  发布在  TypeScript
关注(0)|答案(3)|浏览(160)

我注意到,当从我的服务器/API(在本例中为asp.net core 3.1)接收数据时,Angular会将一个字符串分配给日期目标字段,而不是将其实际“转换”为日期。
就我目前所知,这不是asp.net核心或“服务器端”的问题。
我做了下面的测试,要么我完全理解这里的错误,要么JSON解析器坏了...(我希望不是)?

//The target interface for testing
export interface DateTesting
{
  aText: string;
  aDate: Date;
}

//For testing
let input: DateTesting = { aText: "Hello World!", aDate: new Date() };
console.log(JSON.stringify(input));   //-> outputs the "input" object as JSON

let json = JSON.stringify(input);     //-> {"aText":"Hello World!","aDate":"2022-07-12T12:01:46.498Z"}
let output: DateTesting = JSON.parse(json);

console.log(output);                  //-> outputs the object "output"
console.log(typeof output.aDate);     //-> is a string! Looks like the parser is broken??
console.log(output.aDate.getDay());   //-> Results in an Error: "Uncaught (in promise): Invalid time"

那么,我在这里遗漏了什么呢?当目标类型是Date时,我可以告诉解析器分配一个日期而不是字符串吗?
我也不认为正确的解决方案是对每个日期字段都执行“Date.parse(whatever)”,这也是因为我希望尽可能保持整个过程的“通用性”。
我使用的是Angular 12.1.2

gojuced7

gojuced71#

当目标类型是Date时,我可以告诉json解析器分配一个日期而不是字符串吗?
是的,如果您使用JSON.parse方法和可选的reviver参数,就可以这样做。从文档中 * 如果指定了reiver,解析计算的值在返回之前会进行转换。具体来说,计算值及其所有属性(从最嵌套的属性开始,并继续到原始值本身)单独运行通过reviver。* 因此,在您的特定情况下,您可以指定aDate属性,并将日期字符串作为新的Date对象而不是如下所示的字符串返回:

let output: DateTesting = JSON.parse(json, (key, value) => {
    //instead of a string it returns a Date object
    if (key === 'aDate') return new Date(value);
    return value;     // return the unchanged property value for other keys.
});
console.log(typeof output.aDate);     //-> is a Date object
console.log(output.aDate.getDay()); //ok it works and returns a number
cigdeys3

cigdeys32#

对于覆盖接口中任何Date属性的通用方法,可以使用此装饰器

import {map} from "rxjs/operators";

    let dateRegex = new RegExp(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/); // yyyy-MM-ddTHH:mm:ss
    
    /**
     * Automatically transforms all dates in json in defined regex format to Date entity
     */
    export function TransformDate(target, key: string, descriptor: any) {
      const originalMethod = descriptor.value;
      descriptor.value = function (...args: any[]) {
        return originalMethod.apply(this, args).pipe(
          map((obj) => convertDatesInObject(obj))
        );
      }
      return descriptor;
    }
    
    
    const isDate = (s) => dateRegex.test(s);
    
    function convertDatesInObject(obj): any {
      if (obj === null || obj === undefined) {
        return obj;
      }
      if (isDate(obj)) {
        return new Date(obj) as any;
      }
      if (obj instanceof Array) {
        return obj.map((n: any) => convertDatesInObject(n)) as any;
      }
      if (typeof obj === 'object' && obj !== {}) {
        Object.keys(obj).forEach(k => {
          obj[k] = convertDatesInObject(obj[k]);
        });
        return obj;
      }
      return obj;
    }

然后你可以把这个装饰器放到任何调用API的函数中,所有与定义的regexp匹配的字符串都会被自动解析为Date对象。

@TransformDate
getData$(): Observable<Data[]> {
    return this.httpService.getRequest<Data[]>('yourURL');
}
kd3sttzy

kd3sttzy3#

另一个解决方案是编写一个拦截器,它会自动将所有日期字符串转换为Date对象。

@Injectable({
    providedIn: 'root'
})
export class DateInterceptor implements HttpInterceptor {
    public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(req).pipe(
            map((event: HttpEvent<any>) => {
                if (event instanceof HttpResponse) {
                    this.postProcessDates(event.body);
                    return event;
                }
            })
        );
    }

    /**
     * PostProcessing Dates
     * Converting UTC to Local Date
     * Date must be in ISO 8601 format
     */
    private postProcessDates(obj: any): any {
        if (obj === null || obj === undefined) {
            return obj;
        }

        if (typeof obj !== 'object') {
            return obj;
        }

        for (const key of Object.keys(obj)) {
            const value = obj[key];
            const iso8601 = /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?(([+-]\d\d:\d\d)|Z)?$/;

            if (iso8601.test(value) === true) {
                obj[key] = new Date(value);
            } else if (typeof value === 'object') {
                this.postProcessDates(value);
            }
        }
    }
}

相关问题