Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
zbalkan committed Nov 28, 2024
1 parent 78c4107 commit 6800008
Show file tree
Hide file tree
Showing 8 changed files with 2,592 additions and 255 deletions.
52 changes: 52 additions & 0 deletions source/ditjson/ByteHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Text;

namespace ditjson
{
internal static class ByteHelper
{

/// <summary>
/// Return the string format of a byte array.
/// </summary>
/// <param name="data">
/// The data to format.
/// </param>
/// <returns>
/// A string representation of the data.
/// </returns>
/// <exception cref="FormatException">
/// </exception>
/// <exception cref="OverflowException">
/// </exception>
internal static string FormatBytes(byte[] data)
{
if (data == null)
{
return string.Empty;
}

var sb = new StringBuilder(data.Length * 2);
foreach (var b in data)
{
sb.AppendFormat("{0:x2}", b);
}

return sb.ToString();
}

internal static byte[] ConvertHexStringToBytes(string hexString)
{
if (hexString.Length % 2 != 0)
throw new ArgumentException("Invalid hex string length");

var bytes = new byte[hexString.Length / 2];
for (var i = 0; i < bytes.Length; i++)
{
var byteValue = hexString.Substring(i * 2, 2);
bytes[i] = Convert.ToByte(byteValue, 16);
}
return bytes;
}
}
}
101 changes: 101 additions & 0 deletions source/ditjson/NtdsData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using Microsoft.Isam.Esent.Interop;

namespace ditjson
{
internal static class NtdsData
{
private static readonly JsonSerializerOptions options = new()
{
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.Strict,
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
};

internal static string TablesToJson(Session session, JET_DBID dbid, List<string> tables)
{
var ntdsDictionary = new Dictionary<string, object>();
foreach (var table in tables)
{
ntdsDictionary.Add(table, TableToList(session, dbid, table));
}

string json;
try
{
json = JsonSerializer.Serialize(ntdsDictionary, options);

Check warning on line 29 in source/ditjson/NtdsData.cs

View workflow job for this annotation

GitHub Actions / Release (linux)

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.

Check warning on line 29 in source/ditjson/NtdsData.cs

View workflow job for this annotation

GitHub Actions / Release (macOS)

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.
}
catch (NotSupportedException ex)
{
throw new NtdsException("Failed to serialize to JSON.", ex);
}

return json;
}

/// <summary>
/// Export table as a <see cref="List{Dictionary{string, object}}" />
/// </summary>
/// <param name="session">
/// ESENT Session
/// </param>
/// <param name="dbid">
/// Handle to the database
/// </param>
/// <returns>
/// A <see cref="List{Dictionary{string, object}}" /> containing table data
/// </returns>
/// <exception cref="NtdsException">
/// </exception>
/// <exception cref="FormatException">
/// </exception>
/// <exception cref="OverflowException">
/// </exception>
internal static List<IDictionary<string, object>> TableToList(Session session, JET_DBID dbid, string tableName)
{
var linktableValues = new List<IDictionary<string, object>>();
var columns = new List<ColumnInfo>(Api.GetTableColumns(session, dbid, tableName));

using (var table = new Table(session, dbid, tableName, OpenTableGrbit.ReadOnly))
{
Api.JetSetTableSequential(session, table, SetTableSequentialGrbit.None);
Api.MoveBeforeFirst(session, table);

var columnids = Api.GetColumnDictionary(session, table);
var formattedData = string.Empty;
while (Api.TryMoveNext(session, table))
{
var obj = new Dictionary<string, object>();
foreach (var column in columns)
{
var test = columnids[column.Name];
formattedData = NtdsDataTypes.GetFormattedValue(session, table, column);
var cellValue = formattedData;

// Ignore emptry or null values
if (!string.IsNullOrEmpty(cellValue))
{
if (NtdsMetadata.AttributeMapping.TryGetValue(column.Name, out var mappedValue))
{
obj.Add(mappedValue, cellValue);
}
else
{
obj.Add(column.Name, cellValue);
}
}
}

linktableValues.Add(obj);
}

Api.JetResetTableSequential(session, table, ResetTableSequentialGrbit.None);
}

return linktableValues;
}
}
}
158 changes: 158 additions & 0 deletions source/ditjson/NtdsDataTypes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Isam.Esent.Interop;
using Microsoft.Isam.Esent.Interop.Vista;
using Microsoft.Isam.Esent.Interop.Windows10;

namespace ditjson
{
internal static class NtdsDataTypes
{
/// <summary>
/// Extracts the column data as the correct data type and formats appropriately
/// </summary>
/// <param name="session">
/// </param>
/// <param name="table">
/// </param>
/// <param name="columnInfo">
/// </param>
/// <returns>
/// </returns>
/// <exception cref="NtdsException">
/// </exception>
/// <exception cref="FormatException">
/// </exception>
/// <exception cref="OverflowException">
/// </exception>
internal static string GetFormattedValue(Session session,
JET_TABLEID table,
ColumnInfo columnInfo)
{
var temp = string.Empty;

// Edge case: link_data_v2 column cannot be retreived properly None of the
// Api.RetrieveColumnXXX commands can process this column.
if (columnInfo.Name.Equals("link_data_v2"))
{
return temp;
}

if(columnInfo.Name.Equals("ATTp131353"))
{
//
}

if (columnInfo.Grbit.HasFlag(ColumndefGrbit.ColumnMultiValued))
{
temp = GetMultipleValues(session, table, columnInfo.Columnid, columnInfo.Coltyp);
}
else
{
temp = GetSingleValue(session, table, columnInfo.Columnid, columnInfo.Coltyp);
}

return temp.Replace("\0", string.Empty);
}

private static string GetMultipleValues(Session session, JET_TABLEID table, JET_COLUMNID columnId, JET_coltyp columnType)
{
var values = new List<string>();

var retrievecolumn = new JET_RETRIEVECOLUMN
{
columnid = columnId,
itagSequence = 0
};
Api.JetRetrieveColumns(session, table, [retrievecolumn], 1);
var count = retrievecolumn.itagSequence;

if (count != 0)
{
// itag value starts at 1
for (var itag = 1; itag <= count; ++itag)
{
var s = GetSingleValue(session, table, columnId, columnType);
values.Add(s);
}
}
return values.Count > 0 ? string.Join(",", values) : string.Empty;
}

private static string GetSingleValue(Session session, JET_TABLEID table, JET_COLUMNID columnId, JET_coltyp columnType)
{
var temp = string.Empty;
switch (columnType)
{
case JET_coltyp.Bit:
temp = string.Format("{0}", Api.RetrieveColumnAsBoolean(session, table, columnId));
break;

case VistaColtyp.LongLong:
case Windows10Coltyp.UnsignedLongLong:
case JET_coltyp.Currency:
temp = string.Format("{0}", Api.RetrieveColumnAsInt64(session, table, columnId));
break;

case JET_coltyp.IEEEDouble:
temp = string.Format("{0}", Api.RetrieveColumnAsDouble(session, table, columnId));
break;

case JET_coltyp.IEEESingle:
temp = string.Format("{0}", Api.RetrieveColumnAsFloat(session, table, columnId));
break;

case JET_coltyp.Long:
temp = string.Format("{0}", Api.RetrieveColumnAsInt32(session, table, columnId));
break;

case JET_coltyp.Text:
case JET_coltyp.LongText:
temp = string.Format("{0}", Api.RetrieveColumnAsString(session, table, columnId, Encoding.Unicode, RetrieveColumnGrbit.None));
break;

case JET_coltyp.Short:
temp = string.Format("{0}", Api.RetrieveColumnAsInt16(session, table, columnId));
break;

case JET_coltyp.UnsignedByte:
temp = string.Format("{0}", Api.RetrieveColumnAsByte(session, table, columnId));
break;

case JET_coltyp.DateTime:
temp = string.Format("{0}", Api.RetrieveColumnAsDateTime(session, table, columnId));
break;

case VistaColtyp.UnsignedShort:
temp = string.Format("{0}", Api.RetrieveColumnAsUInt16(session, table, columnId));
break;

case VistaColtyp.UnsignedLong:
temp = string.Format("{0}", Api.RetrieveColumnAsUInt32(session, table, columnId));
break;

case VistaColtyp.GUID:
temp = string.Format("{0}", Api.RetrieveColumnAsGuid(session, table, columnId));
break;

case JET_coltyp.Nil:
break;

case JET_coltyp.Binary:
case JET_coltyp.LongBinary:
var columnBytes = Api.RetrieveColumn(session, table, columnId);
if (columnBytes != null)
{
temp = ByteHelper.FormatBytes(columnBytes);
}
break;

default:
throw new NtdsException($"Unhandled column type {columnType} for {columnId}");
}

return temp;
}
}
}
4 changes: 2 additions & 2 deletions source/ditjson/NtdsException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace ditjson
{
/// <inheritdoc/>
/// <inheritdoc />
[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
public class NtdsException : Exception
{
Expand All @@ -21,4 +21,4 @@ public NtdsException(string? message, Exception? innerException) : base(message,

private string GetDebuggerDisplay() => ToString();
}
}
}
Loading

0 comments on commit 6800008

Please sign in to comment.