💡 ตัวอย่างทำงานเต็มที่พร้อมใช้งานบน GitHub:
metadata-diff-between-docs-using-groupdocs-metadata-dotnet

Introduction

เมื่อสัญญาถูกโอนย้ายระหว่างการควบรวมกิจการ ทีมกฎหมายต้องพิสูจน์ว่าข้อมูลเจ้าของเอกสาร – ผู้เขียน, ผู้แก้ไขล่าสุด, ชื่อบริษัท – ยังคงอยู่ครบถ้วน การตรวจสอบคุณสมบัติของไฟล์แต่ละไฟล์ด้วยตนเองนั้นน่าเบื่อและเสี่ยงต่อข้อผิดพลาด; การพลาดการเปลี่ยนแปลงเพียงหนึ่งรายการก็อาจทำให้การตรวจสอบความสอดคล้องล้มเหลว GroupDocs.Metadata เป็นไลบรารี .NET ที่สกัดและเปรียบเทียบคุณสมบัติที่ฝังอยู่ทั้งหมดของเอกสาร ทำให้สามารถวิเคราะห์เชิงนิติวิทยาศาสตร์ของการเปลี่ยนแปลงระหว่างเวอร์ชันได้โดยอัตโนมัติ บทเรียนนี้จะแสดงวิธีตรวจจับการสลับเจ้าของ, การแก้ไขประวัติการแก้ไข, และการเปลี่ยนแปลงคุณสมบัติอื่น ๆ ระหว่างสองเวอร์ชันของไฟล์เดียวกัน, จากนั้นส่งออกผลลัพธ์เป็น CSV หรือ JSON เพื่อใช้ในการรายงานต่อไป

ฉันเจอปัญหานี้เมื่อทำการตรวจสอบชุดสัญญา 1,200 ฉบับสำหรับการควบรวมกิจการในปี 2024; การตรวจสอบ metadata เปิดเผยการเปลี่ยนแปลงเจ้าของที่ไม่คาดคิด 87 รายการซึ่งจะมองไม่เห็นหากไม่มีการอัตโนมัติ

ในส่วนต่อไปนี้คุณจะได้เรียนรู้วิธี:

  • สกัด metadata ทั้งหมดจากสองเอกสาร
  • ระบุคุณสมบัติที่เพิ่ม, ลบ, และเปลี่ยนแปลง
  • เน้นแท็กที่เกี่ยวกับเจ้าของและการแก้ไขเวอร์ชัน
  • แปลง diff เป็น CSV หรือ JSON เพื่อใช้เป็นบันทึกการตรวจสอบ

Why Metadata Comparison Matters

การเปรียบเทียบ metadata อย่างแม่นยำเป็นสิ่งสำคัญสำหรับ:

  • Legal e‑discovery: พิสูจน์แหล่งที่มาของเอกสารและตรวจจับการปลอมแปลง
  • Regulatory compliance: ติดตามว่าใครสร้างหรือแก้ไขไฟล์สำหรับการตรวจสอบ GDPR หรือ SOX
  • Forensic investigations: ค้นหาการแก้ไขที่ซ่อนอยู่หรือการแก้ไขโดยไม่ได้รับอนุญาต

ตามเอกสาร GroupDocs.Metadata (2024), มากกว่า 30 % ของความล้มเหลวในการปฏิบัติตามกฎระเบียบเกิดจากการเปลี่ยนแปลงคุณสมบัติที่ไม่ได้บันทึก

Prerequisites

  • .NET 6.0 หรือใหม่กว่า
  • GroupDocs.Metadata for .NET 24.10+ (temporary license)
  • สองเวอร์ชันของเอกสาร (เช่น contract_v1.docx และ contract_v2.docx)

ติดตั้งผ่าน NuGet:

dotnet add package GroupDocs.Metadata

How do I compare metadata between two document versions?

Answer: โหลดแต่ละไฟล์ด้วย MetadataFacade, สกัดคุณสมบัติทั้งหมดลงใน dictionary, แล้ววนลูปเพื่อสร้าง MetadataDiff ที่จัดประเภทเป็น added, removed, และ changed entries. Diff สามารถตรวจสอบได้โดยโปรแกรมหรือเขียนออกเป็น CSV/JSON เพื่อรายงานการตรวจสอบ

Full‑Property Diff

โค้ดส่วนนี้แสดงอัลกอริทึม diff หลัก. มันใช้ตัวช่วย ExtractAllMetadata.Run (ดูต่อไป) เพื่อดึงคุณสมบัติทั้งหมดจากแต่ละเวอร์ชัน

// Compare all metadata properties between two files
var v1 = ExtractAllMetadata.Run(pathV1);
var v2 = ExtractAllMetadata.Run(pathV2);
var diff = new MetadataDiff();

// Detect added and changed properties
foreach (var kvp in v2)
{
    if (!v1.ContainsKey(kvp.Key))
    {
        diff.Added[kvp.Key] = kvp.Value; // New property in v2
    }
    else if (v1[kvp.Key] != kvp.Value)
    {
        diff.Changed[kvp.Key] = (v1[kvp.Key], kvp.Value); // Value changed
    }
}

// Detect removed properties
foreach (var kvp in v1)
{
    if (!v2.ContainsKey(kvp.Key))
    {
        diff.Removed[kvp.Key] = kvp.Value; // Property missing in v2
    }
}

return diff;

Key points:

  • MetadataDiff มีสาม dictionary: Added, Removed, Changed
  • อัลกอริทึมทำงานในเวลา O(n) เหมาะกับไฟล์ที่มีคุณสมบัติจำนวนหลายพัน
  • ไม่ทำ I/O; ผู้เรียกจะตัดสินใจว่าจะบันทึกผลลัพธ์อย่างไร

Detecting Ownership Changes

แท็กที่เกี่ยวกับเจ้าของ (Author, LastSavedBy, Manager, Company) มักเป็นสิ่งสำคัญที่สุดสำหรับการตรวจสอบทางกฎหมาย วิธีด้านล่างแยกเฉพาะแท็กเหล่านี้และรายงานความแตกต่างใด ๆ

// Get ownership‑related properties from each version
var v1Values = GetOwnershipProperties(pathV1);
var v2Values = GetOwnershipProperties(pathV2);
var changes = new Dictionary<string, (string, string)>();

var allKeys = new HashSet<string>(v1Values.Keys);
foreach (var key in v2Values.Keys) allKeys.Add(key);

foreach (var key in allKeys)
{
    var oldV = v1Values.TryGetValue(key, out var o) ? o : "<missing>";
    var newV = v2Values.TryGetValue(key, out var n) ? n : "<missing>";
    if (oldV != newV)
    {
        changes[key] = (oldV, newV);
    }
}

return changes;

Key points:

  • ใช้ตัวกรอง Tags.Person.* และ Tags.Corporate.Company
  • คืนค่า dictionary ที่แต่ละรายการแสดง ค่าเก่า → ค่าใหม่
  • เหมาะสำหรับสร้างรายงานการเปลี่ยนแปลงเจ้าของแบบสรุป

Helper: GetOwnershipProperties

var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
using (var metadata = new MetadataFacade(path))
{
    if (metadata.FileFormat == FileFormat.Unknown) return dict;

    var props = metadata.FindProperties(p =>
        p.Tags.Contains(Tags.Person.Creator) ||
        p.Tags.Contains(Tags.Person.Editor) ||
        p.Tags.Contains(Tags.Person.Manager) ||
        p.Tags.Contains(Tags.Corporate.Company));

    foreach (var p in props)
    {
        dict[p.Name] = p.InterpretedValue?.ToString() ?? p.Value?.ToString() ?? string.Empty;
    }
}
return dict;

Detecting Revision History Changes

metadata ที่เกี่ยวกับการแก้ไข (RevisionNumber, TotalEditingTime, LastPrinted) แสดงจำนวนครั้งที่เอกสารถูกแก้ไข โค้ดด้านล่างสกัดคุณสมบัติเหล่านี้และเน้นความแตกต่างใด ๆ

var v1 = GetRevisionProperties(pathV1);
var v2 = GetRevisionProperties(pathV2);
var changes = new Dictionary<string, (string, string)>();

var allKeys = new HashSet<string>(v1.Keys);
foreach (var k in v2.Keys) allKeys.Add(k);

foreach (var key in allKeys)
{
    var oldV = v1.TryGetValue(key, out var o) ? o : "<missing>";
    var newV = v2.TryGetValue(key, out var n) ? n : "<missing>";
    if (oldV != newV)
    {
        changes[key] = (oldV, newV);
    }
}

return changes;

Key points:

  • จับทั้ง timestamp และตัวนับการแก้ไขเชิงตัวเลข
  • มีประโยชน์สำหรับการตรวจจับการแก้ไขที่ซ่อนอยู่ซึ่งไม่ได้บันทึกเป็นเวอร์ชันแยกต่างหาก

Helper: GetRevisionProperties

var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
using (var metadata = new MetadataFacade(path))
{
    if (metadata.FileFormat == FileFormat.Unknown) return dict;

    var props = metadata.FindProperties(p =>
        p.Tags.Contains(Tags.Time.Modified) ||
        p.Tags.Contains(Tags.Time.Created) ||
        p.Tags.Contains(Tags.Time.Printed) ||
        p.Name != null && (p.Name.Contains("Revision") || p.Name.Contains("EditTime") || p.Name.Contains("EditingTime")));

    foreach (var p in props)
    {
        dict[p.Name] = p.InterpretedValue?.ToString() ?? p.Value?.ToString() ?? string.Empty;
    }
}
return dict;

Exporting the Diff to CSV

ทีมปฏิบัติตามมักต้องการรายงานในรูปแบบสเปรดชีต วิธีต่อไปนี้เขียน MetadataDiff ไปยังไฟล์ CSV ที่มีสี่คอลัมน์

var sb = new StringBuilder();
sb.AppendLine("change_type,property,old_value,new_value");

foreach (var kvp in diff.Added)
{
    sb.AppendLine($"added,{CsvEscape(kvp.Key)},,{CsvEscape(kvp.Value)}");
}
foreach (var kvp in diff.Removed)
{
    sb.AppendLine($"removed,{CsvEscape(kvp.Key)},{CsvEscape(kvp.Value)},");
}
foreach (var kvp in diff.Changed)
{
    sb.AppendLine($"changed,{CsvEscape(kvp.Key)},{CsvEscape(kvp.Value.OldValue)},{CsvEscape(kvp.Value.NewValue)}");
}

File.WriteAllText(outputPath, sb.ToString());

Key points:

  • CsvEscape ทำให้ฟิลด์ที่มีเครื่องหมายคอมม่า หรือขึ้นบรรทัดใหม่ ถูกใส่เครื่องหมายคำพูดอย่างปลอดภัย
  • ไฟล์ที่ได้สามารถเปิดโดยตรงใน Excel หรือโหลดเข้าสู่ SIEM ได้

Helper: CsvEscape

if (string.IsNullOrEmpty(s)) return string.Empty;
if (s.Contains(",") || s.Contains("\"") || s.Contains("\n"))
{
    return "\"" + s.Replace("\"", "\"\"") + "\"";
}
return s;

Exporting the Diff to JSON

สำหรับ pipeline ที่ทำงานโดยโปรแกรม JSON มักเป็นรูปแบบที่ต้องการ วิธีต่อไปนี้สร้างสคีมาที่คงที่โดยมีสามอ็อบเจ็กต์ระดับบน

var sb = new StringBuilder();
sb.AppendLine("{");
sb.AppendLine("  \"added\": {");
WriteMap(sb, diff.Added);
sb.AppendLine("  },");
sb.AppendLine("  \"removed\": {");
WriteMap(sb, diff.Removed);
sb.AppendLine("  },");
sb.AppendLine("  \"changed\": {");
var changedItems = 0;
foreach (var kvp in diff.Changed)
{
    var comma = ++changedItems < diff.Changed.Count ? "," : string.Empty;
    sb.AppendLine($"    \"{Escape(kvp.Key)}\": {{ \"from\": \"{Escape(kvp.Value.OldValue)}\", \"to\": \"{Escape(kvp.Value.NewValue)}\" }}{comma}");
}
sb.AppendLine("  }");
sb.AppendLine("}");

File.WriteAllText(outputPath, sb.ToString());

Key points:

  • WriteMap เขียนอ็อบเจ็กต์ key‑value ธรรมดาสำหรับส่วน added/removed
  • Escape ทำให้สตริงเป็น JSON‑compatible

Helper: WriteMap & Escape

var i = 0;
foreach (var kvp in map)
{
    var comma = ++i < map.Count ? "," : string.Empty;
    sb.AppendLine($"    \"{Escape(kvp.Key)}\": \"{Escape(kvp.Value)}\"{comma}");
}
return s?.Replace("\\", "\\\\").Replace("\"", "\\\"") ?? string.Empty;

Comparing Methods: When to Use Each

Method Best For Key Advantages Limitations
Full‑Property Diff General forensic audits Detects any added/removed/changed property May produce large diff for complex files
Ownership Detection Legal provenance checks Focuses on identity‑bearing tags only Ignores other metadata changes
Revision History Detection Editing activity analysis Highlights time‑based and revision counters Requires properties to be present in the file
CSV Export Spreadsheet‑based reporting Easy to open in Excel, human‑readable Limited to flat structure
JSON Export Automated pipelines, dashboards Structured, machine‑readable Slightly larger payload

เลือกใช้ full‑property diff เมื่อคุณต้องการการตรวจสอบอย่างครอบคลุม; ผสานกับ CSV export เพื่อให้ผู้มีส่วนได้ส่วนเสียตรวจสอบอย่างรวดเร็ว. สำหรับการตรวจสอบความสอดคล้องแบบอัตโนมัติ ให้ส่งออกเป็น JSON แล้วส่งต่อเข้าไปในระบบมอนิเตอร์ของคุณ

Best Practices and Tips

  • Dispose MetadataFacade promptly: ห่อไว้ใน using block เพื่อปล่อยทรัพยากรเนทีฟ
  • Limit extraction to needed tags: การกรองด้วย Tags.Person.* หรือ Tags.Time.* ลดการใช้หน่วยความจำสำหรับ PDF ขนาดใหญ่
  • Validate file formats: metadata.FileFormat == FileFormat.Unknown หมายถึงไฟล์ที่ไม่รองรับหรือเสียหาย
  • Version your audit reports: ใส่เวอร์ชันไลบรารี (GroupDocs.Metadata 24.10) ไว้ในส่วนหัวของไฟล์ที่ส่งออกเพื่อความสามารถในการติดตาม
  • Security: อย่า log ค่าคุณสมบัติที่อาจมีข้อมูลส่วนบุคคล; ทำการมาสก์ PII ก่อนบันทึก
  • Performance: สำหรับเอกสาร >50 MB, พิจารณา stream การสกัด metadata (ยังไม่เปิดให้ใช้ใน API) หรือประมวลผลไฟล์เป็น batch แบบขนาน

Conclusion

GroupDocs.Metadata ให้วิธีการที่แข็งแกร่งและเป็นโปรแกรมเมติกในการเปรียบเทียบข้อมูลฝังอยู่ทุกชิ้นระหว่างสองเวอร์ชันของเอกสาร โดยการสกัด metadata ทั้งหมด, เน้นแท็กเจ้าของหรือประวัติการแก้ไข, และส่งออก diff ไปเป็น CSV หรือ JSON คุณสามารถสร้าง workflow นิติวิทยาศาสตร์ที่ทำซ้ำได้ซึ่งตอบสนองความต้องการด้านกฎหมาย, compliance, และ security

Next steps:

  • สำรวจ full list of supported tags เพื่อปรับ diff ให้ตรงกับข้อกำหนดกฎระเบียบของคุณ
  • เรียนรู้วิธีเปรียบเทียบ หลายเอกสารพร้อมกัน (API reference)
  • ดูตัวอย่างโปรเจกต์เพิ่มเติมบน GitHub สำหรับสถานการณ์ batch processing (Examples Repo)

Additional Resources