Skip to content

Commit d27a4fc

Browse files
committed
feat: resolve JSON pointer path for a relative reference
1 parent 23c5580 commit d27a4fc

File tree

2 files changed

+34
-5
lines changed

2 files changed

+34
-5
lines changed

src/Microsoft.OpenApi/Models/OpenApiReference.cs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,14 +314,42 @@ internal void SetSummaryAndDescriptionFromMapNode(MapNode mapNode)
314314
}
315315
}
316316

317-
internal void SetJsonPointerPath(string pointer)
317+
internal void SetJsonPointerPath(string pointer, string nodeLocation)
318318
{
319-
// Eg of an internal subcomponent's JSONPath: #/components/schemas/person/properties/address
320-
if ((pointer.Contains('#') || pointer.StartsWith("http", StringComparison.OrdinalIgnoreCase))
321-
&& !string.IsNullOrEmpty(ReferenceV3) && !ReferenceV3!.Equals(pointer, StringComparison.OrdinalIgnoreCase))
319+
// Relative reference to internal JSON schema node/resource (e.g. "#/properties/b")
320+
if (pointer.StartsWith("#/", StringComparison.OrdinalIgnoreCase) && !pointer.Contains("/components/schemas"))
321+
{
322+
ReferenceV3 = ResolveRelativePointer(nodeLocation, pointer);
323+
}
324+
325+
// Absolute reference or anchor (e.g. "#/components/schemas/..." or full URL)
326+
else if ((pointer.Contains('#') || pointer.StartsWith("http", StringComparison.OrdinalIgnoreCase))
327+
&& !string.Equals(ReferenceV3, pointer, StringComparison.OrdinalIgnoreCase))
322328
{
323329
ReferenceV3 = pointer;
330+
}
331+
}
332+
333+
private static string ResolveRelativePointer(string nodeLocation, string relativeRef)
334+
{
335+
// Convert nodeLocation to path segments
336+
var segments = nodeLocation.TrimStart('#').Split(['/'], StringSplitOptions.RemoveEmptyEntries).ToList();
337+
338+
// Convert relativeRef to dynamic segments
339+
var relativeSegments = relativeRef.TrimStart('#').Split(['/'], StringSplitOptions.RemoveEmptyEntries);
340+
341+
// Locate the first occurrence of relativeRef segments in the full path
342+
for (int i = 0; i <= segments.Count - relativeSegments.Length; i++)
343+
{
344+
if (relativeSegments.SequenceEqual(segments.Skip(i).Take(relativeSegments.Length)))
345+
{
346+
// Trim to include just the matching segment chain
347+
segments = [.. segments.Take(i + relativeSegments.Length)];
348+
break;
349+
}
324350
}
351+
352+
return $"#/{string.Join("/", segments)}";
325353
}
326354
}
327355
}

src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,13 +367,14 @@ public static IOpenApiSchema LoadSchema(ParseNode node, OpenApiDocument hostDocu
367367

368368
var pointer = mapNode.GetReferencePointer();
369369
var identifier = mapNode.GetJsonSchemaIdentifier();
370+
var nodeLocation = node.Context.GetLocation();
370371

371372
if (pointer != null)
372373
{
373374
var reference = GetReferenceIdAndExternalResource(pointer);
374375
var result = new OpenApiSchemaReference(reference.Item1, hostDocument, reference.Item2);
375376
result.Reference.SetSummaryAndDescriptionFromMapNode(mapNode);
376-
result.Reference.SetJsonPointerPath(pointer);
377+
result.Reference.SetJsonPointerPath(pointer, nodeLocation);
377378
return result;
378379
}
379380

0 commit comments

Comments
 (0)