💡 ตัวอย่างการทำงานเต็มที่พร้อมใช้งานบน 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/removedEscapeทำให้สตริงเป็นรูปแบบที่เข้ากับ 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ทันที: ใช้usingblock เพื่อปล่อยทรัพยากรเนทีฟ - จำกัดการสกัดให้เฉพาะแท็กที่ต้องการ: การกรองด้วย
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)