| File: ExtractionDocument.cs | Web Access |
| Project: src\src\Libraries\Microsoft.Extensions.Documents.Abstractions\Microsoft.Extensions.Documents.Abstractions.csproj (Microsoft.Extensions.Documents.Abstractions) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Text.Json.Serialization; using Microsoft.Extensions.AI; using Microsoft.Shared.Diagnostics; namespace Microsoft.Extensions.Documents; /// <summary> /// Represents an ordered, provider-neutral structured document. /// </summary> public sealed class ExtractionDocument { /// <summary> /// Initializes a new instance of the <see cref="ExtractionDocument"/> class. /// </summary> /// <param name="identifier">The local identifier of the document.</param> /// <param name="content">The local payload, or <see langword="null"/> for a structural document.</param> public ExtractionDocument(string identifier, AIContent? content = null) { Identifier = Throw.IfNullOrEmpty(identifier); Content = content; } /// <summary>Gets the document identifier.</summary> public string Identifier { get; } /// <summary>Gets or sets the local payload of the document, excluding its sections.</summary> /// <remarks>Payloads are borrowed and mutable; their interpretation is determined by the producer.</remarks> public AIContent? Content { get; set; } /// <summary>Gets the ordered root sections of the document.</summary> public List<ExtractionDocumentSection> Sections { get; } = []; /// <summary>Gets the metadata associated with the document.</summary> public Dictionary<string, object?> Metadata { get; } = new(StringComparer.Ordinal); /// <summary>Gets source frames, independently of assembled section order.</summary> /// <remarks>Producers explicitly add frames, including blank frames with no content elements.</remarks> public List<ExtractionDocumentSourcePage> SourcePages { get; } = []; /// <summary>Gets or sets the optional borrowed native document result.</summary> /// <remarks> /// Native data is not implicitly disposed, deep-cloned, logged or serialized. /// This does not guarantee original response bytes or durable serialization. /// </remarks> [JsonIgnore] public object? RawRepresentation { get; set; } /// <summary>Enumerates non-section content in depth-first document order.</summary> /// <remarks> /// This structural traversal does not flatten table cells or return document/section-local payloads. /// Use the hierarchy to inspect those local payloads and table cells directly. /// Cyclic section relationships and null entries are rejected when traversed. /// </remarks> /// <returns>The content elements in document order.</returns> public IEnumerable<ExtractionDocumentElement> EnumerateContent() { HashSet<ExtractionDocumentSection> path = []; foreach (ExtractionDocumentSection section in Sections) { foreach (ExtractionDocumentElement element in EnumerateSection(section, path)) { yield return element; } } } private static IEnumerable<ExtractionDocumentElement> EnumerateSection( ExtractionDocumentSection section, HashSet<ExtractionDocumentSection> path) { if (section is null || !path.Add(section)) { throw new InvalidOperationException("A document cannot traverse a null section or a cyclic section relationship."); } try { foreach (ExtractionDocumentElement element in section.Elements) { if (element is null) { throw new InvalidOperationException("A document cannot traverse a null element."); } if (element is ExtractionDocumentSection nestedSection) { foreach (ExtractionDocumentElement nestedElement in EnumerateSection(nestedSection, path)) { yield return nestedElement; } } else { yield return element; } } } finally { _ = path.Remove(section); } } }