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

Introduction

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

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

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

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

Why Metadata Comparison Matters

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

  • 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)

Install via NuGet:

dotnet add package GroupDocs.Metadata

How do I compare metadata between two document versions?

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

Full‑Property 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 มีพจนานุกรมสามตัว: 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
  • คืนค่าพจนานุกรมที่แต่ละรายการแสดงค่า เก่า → ใหม่
  • เหมาะสำหรับการสร้างรายงานการเปลี่ยนแปลงเจ้าของแบบสรุป

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

เมตาดาต้าที่เกี่ยวกับการแก้ไข (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:

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

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

สำหรับสายงานที่ทำงานแบบโปรแกรม, 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 เขียนอ็อบเจ็กต์แบบคีย์‑ค่าแบบง่ายสำหรับส่วน added/removed
  • Escape ทำให้สตริงเป็นรูปแบบที่เข้ากับ JSON ได้

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 การตรวจสอบนิติวิทยาศาสตร์ทั่วไป ตรวจจับคุณสมบัติที่เพิ่ม/ลบ/เปลี่ยนแปลงทั้งหมด อาจสร้าง diff ขนาดใหญ่สำหรับไฟล์ที่ซับซ้อน
Ownership Detection การตรวจสอบแหล่งที่มาทางกฎหมาย เน้นแท็กที่บ่งบอกตัวตนเท่านั้น ไม่สนใจการเปลี่ยนแปลงเมตาดาต้าอื่น
Revision History Detection การวิเคราะห์กิจกรรมการแก้ไข เน้นตัวนับและเวลาที่เกี่ยวกับการแก้ไข ต้องมีคุณสมบัตินั้นอยู่ในไฟล์
CSV Export รายงานแบบสเปรดชีต เปิดง่ายใน Excel, อ่านได้โดยมนุษย์ โครงสร้างแบน จำกัด
JSON Export สายงานอัตโนมัติ, แดชบอร์ด มีโครงสร้าง, อ่านได้โดยเครื่อง ขนาด payload มากกว่าหน่อย

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

Best Practices and Tips

  • Dispose MetadataFacade ทันที: ใช้ using block เพื่อปล่อยทรัพยากรเนทีฟ
  • จำกัดการสกัดให้เฉพาะแท็กที่ต้องการ: การกรองด้วย Tags.Person.* หรือ Tags.Time.* ลดการใช้หน่วยความจำสำหรับ PDF ขนาดใหญ่
  • ตรวจสอบรูปแบบไฟล์: metadata.FileFormat == FileFormat.Unknown หมายถึงไฟล์ที่ไม่รองรับหรือไฟล์เสีย
  • เวอร์ชันรายงานการตรวจสอบ: ใส่เวอร์ชันของไลบรารี (GroupDocs.Metadata 24.10) ในส่วนหัวของไฟล์ที่ส่งออกเพื่อความสามารถในการติดตาม
  • ความปลอดภัย: อย่า log ค่าคุณสมบัติแบบดิบที่อาจมีข้อมูลส่วนบุคคล; ให้ทำการมาสก์ PII ก่อนบันทึก
  • ประสิทธิภาพ: สำหรับเอกสาร >50 MB, พิจารณาใช้การสตรีมเมตาดาต้า (ยังไม่เปิดให้ใช้ใน API) หรือประมวลผลไฟล์เป็นชุดแบบขนาน

Conclusion

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

Next steps:

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

Additional Resources