It seems to be a Base64-encoded string of durableId in CommentID.
namespace WordOpenXMLSDK
{
using System;
using System.Linq;
using System.Collections.Generic;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
internal class Program
{
static void Main(string[] args)
{
var docxPath = @"Test.docx";
var doc = DocumentFormat.OpenXml.Packaging.WordprocessingDocument.Open(docxPath, false);
var comments = Comment.GetList(doc);
var commentIds = CommentId.GetList(doc);
foreach (var commentId in commentIds)
{
Comment? comment = comments.FirstOrDefault(_ => _.Paragraphs.Contains(commentId.paraId));
Console.WriteLine($"{commentId.ToNavString()}\tTime=>{comment?.Time}\tText={comment?.Text}");
}
}
class Comment
{
public DateTime? Time;
public string Text;
public readonly List<UInt64> Paragraphs = new List<UInt64>();
public static List<Comment> GetList(DocumentFormat.OpenXml.Packaging.WordprocessingDocument doc)
{
List<Comment> list = new List<Comment>();
var comments = doc?.MainDocumentPart?.WordprocessingCommentsPart.Comments;
if (comments == null)
{
return list;
}
foreach (var c in comments.OfType<DocumentFormat.OpenXml.Wordprocessing.Comment>())
{
Comment com = new Comment();
com.Time = c.DateUtc?.Value ?? c.Date?.Value;
com.Text = c.InnerText;
foreach (var p in c.ChildElements.OfType<DocumentFormat.OpenXml.Wordprocessing.Paragraph>())
{
if (p?.ParagraphId != null && TryGetUint64(p.ParagraphId, out var paraID))
{
com.Paragraphs.Add(paraID);
}
}
list.Add(com);
}
return list;
}
}
class CommentId
{
public UInt64 paraId;
public UInt64 durableId;
public string ToNavString()
{
var stext = "{\"c\":" + durableId.ToString() + "}";
var base64 = System.Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(stext));
return "nav=" + base64;
}
public static List<CommentId> GetList(DocumentFormat.OpenXml.Packaging.WordprocessingDocument doc)
{
List<CommentId> list = new List<CommentId>();
var commentsIds = doc.MainDocumentPart?.WordprocessingCommentsIdsPart?.CommentsIds;
if (commentsIds == null)
{
return list;
}
foreach (var commentId in commentsIds.ChildElements.OfType<DocumentFormat.OpenXml.Office2019.Word.Cid.CommentId>())
{
var comment = new CommentId();
if (commentId.ParaId == null || !TryGetUint64(commentId.ParaId, out comment.paraId))
{
continue;
}
if (commentId.DurableId == null || !TryGetUint64(commentId.DurableId, out comment.durableId))
{
continue;
}
list.Add(comment);
}
return list;
}
}
class CommentExtensible
{
public UInt64 durableId;
public DateTime? dateUtc;
public static List<CommentExtensible> GetList(DocumentFormat.OpenXml.Packaging.WordprocessingDocument doc)
{
List<CommentExtensible> list = new List<CommentExtensible>();
var extensibles = doc.MainDocumentPart?.WordCommentsExtensiblePart?.CommentsExtensible;
if (extensibles == null)
{
return list;
}
foreach (var commentExt in extensibles.OfType<DocumentFormat.OpenXml.Office2021.Word.CommentsExt.CommentExtensible>())
{
var cex = new CommentExtensible();
if (commentExt.DurableId == null || !TryGetUint64(commentExt.DurableId, out cex.durableId))
{
continue;
}
cex.dateUtc = commentExt.DateUtc?.Value;
list.Add(cex);
}
return list;
}
}
private static bool TryGetUint64(HexBinaryValue hex, out UInt64 u64)
{
u64 = 0;
if (hex == null || !hex.TryGetBytes(out byte[]? bytes) || bytes == null || bytes.Length == 0)
{
return false;
}
if (bytes.Length > 8)
{
throw new NotSupportedException();
}
if (BitConverter.IsLittleEndian)
{
Array.Reverse(bytes);
}
byte[] bs = new byte[8];
Array.Copy(bytes, bs, bytes.Length);
u64 = System.BitConverter.ToUInt64(bs, 0);
return true;
}
}
}