Resolve JSON Reference for ASP.NET backend
Did you encounter the “ref” information from backend of Asp.net core?
0:{$id: '2', id: 26, roomId: 1, guestId: 1, checkInDate: '2025-09-18T00:00:00', …}
1:{$ref: '6'}
2:{$ref: '12'}
3:{$ref: '9'}
4:{$ref: '16'}
This JSON structure (with $ref
and $id
) indicates that your API is using JSON Reference to handle circular dependencies or shared objects (a common approach when serializing entities with relationships, like Booking
→ Room
→ Booking
). Here’s how to fix Angular’s handling of these references:
What’s Happening
$id: '2'
marks an object as uniquely identifiable (e.g., the firstBooking
).{$ref: '6'}
tells Angular: “This object is a reference to the object with$id: '6'
”.
Angular doesn’t natively parse $ref
/$id
by default, so it treats these as plain objects (leading to undefined
or broken data for referenced records).
How to Fix It
You need to resolve JSON references in Angular before using the data. Here’s how:
Step 1: Install a JSON Reference Resolver
Use a library like json-refs
to resolve references. Install it via npm:
npm install json-refs
Step 2: Create a Helper Service to Resolve References
Create a service to fetch data and resolve $ref
/$id
before passing it to components:
// reference-resolver.service.ts
import { Injectable } from '@angular/core';
import * as JsonRefs from 'json-refs';@Injectable({providedIn: 'root'
})
export class ReferenceResolverService {resolveReferences<T>(data: any): T {const options = {resolveCirculars: true, // Handle circular referencesloaderOptions: {processContent: (res: any) => JSON.parse(res.text())}};return JsonRefs.resolveRefs(data, options).then((result: any) => {return result.resolved as T;});}
}
Step 3: Use the Resolver in Your Booking Service
Modify your service to resolve references before returning data:
// booking.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ReferenceResolverService } from './reference-resolver.service';
import { Booking } from './booking.model';@Injectable({providedIn: 'root'
})
export class BookingService {private apiUrl = 'https://your-api-url/bookings';constructor(private http: HttpClient,private refResolver: ReferenceResolverService) { }getBookings(): Observable<Booking[]> {return this.http.get<any>(this.apiUrl).pipe(map((apiResponse: any) => this.refResolver.resolveReferences<Booking[]>(apiResponse)));}
}
Step 4: Ensure Your Interface Matches the Resolved Data
Update your Booking
interface to match the structure after references are resolved (no more $ref
/$id
):
// booking.model.ts
export interface Booking {id: number;roomId: number;guestId: number;checkInDate: string;checkOutDate: string;room: Room; // Now a concrete object (not a $ref)guest: Guest; // Now a concrete object (not a $ref)
}export interface Room {id: number;name: string;// Other room properties
}export interface Guest {id: number;name: string;// Other guest properties
}
Why This Works
json-refs
processes the API response, replacing$ref
with the actual referenced object.- Angular now receives a plain array of
Booking
objects (no more$ref
/$id
), so all records render correctly.
Alternative: Configure Backend to Avoid $ref
If you prefer the backend to send “flat” JSON (no references), configure Entity Framework/JSON serialization to avoid generating $ref
/$id
:
In ASP.NET Core (.NET 6+
), update Program.cs
to ignore circular references (which disables $ref
generation):
builder.Services.AddControllers().AddJsonOptions(options => {options.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles;});
This tells the serializer to duplicate objects instead of using references, so Angular receives plain JSON (no $ref
/$id
). Use this if you don’t want to add a reference resolver in Angular.
By resolving JSON references (either in Angular or via backend configuration), all Booking
records will load correctly without $ref
/$id
issues.