Skip to content

Instantly share code, notes, and snippets.

@rjmholt
Created December 9, 2019 21:18
Show Gist options
  • Save rjmholt/dfb403556753f17fedd3075edd91459f to your computer and use it in GitHub Desktop.
Save rjmholt/dfb403556753f17fedd3075edd91459f to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Management.Automation;
using System.Reflection;
using System.Text;
namespace RobPsUtils
{
public abstract class PSObjectSerializer
{
private static BindingFlags s_publicInstance = BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.FlattenHierarchy;
public PSObjectSerializer()
{
StrBuffer = new StringBuilder();
CurrentObjectDepth = 0;
}
protected StringBuilder StrBuffer { get; }
protected virtual int DepthLimit { get; } = 10;
private int CurrentObjectDepth { get; set; }
public string Convert(object obj)
{
Reset();
StatefulConvert(obj);
return StrBuffer.ToString();
}
protected abstract void StartArray();
protected abstract void EndArray();
protected abstract void SeparateArray();
protected abstract void StartObject();
protected abstract void EndObject();
protected abstract void SeparateObject();
protected abstract void RelateObjectKeyValue();
protected abstract void WriteString(string s);
protected virtual void WriteDepthLimitedObject(object obj)
{
WriteString(obj.ToString());
}
protected virtual void WriteKey(string key)
{
WriteString(key);
}
protected virtual void Reset()
{
StrBuffer.Clear();
CurrentObjectDepth = 0;
}
private void StatefulConvert(object obj, bool isKey = false)
{
CurrentObjectDepth++;
try
{
if (TryConvertPrimitive(obj, isKey)) { return; }
if (CurrentObjectDepth > DepthLimit)
{
WriteString(obj.ToString());
return;
}
if (TryConvertTable(obj)) { return; }
if (TryConvertArray(obj)) { return; }
ConvertObject(obj);
}
finally
{
CurrentObjectDepth--;
}
}
private bool TryConvertPrimitive(object obj, bool isKey)
{
switch (obj)
{
case null:
StrBuffer.Append("$null");
return true;
case string s:
if (isKey)
{
WriteKey(s);
}
else
{
WriteString(s);
}
return true;
case bool b:
StrBuffer.Append('$').Append(b);
return true;
case int i:
StrBuffer.Append(i);
return true;
case double d:
StrBuffer.Append(d);
return true;
}
return false;
}
private bool TryConvertArray(object obj)
{
if (!(obj is IEnumerable enumerable))
{
return false;
}
StartArray();
bool first = true;
foreach (object element in enumerable)
{
if (!first)
{
SeparateArray();
}
first = false;
StatefulConvert(element);
}
EndArray();
return true;
}
private bool TryConvertTable(object obj)
{
if (obj is IDictionary dict)
{
StartObject();
bool first = true;
foreach (DictionaryEntry item in dict)
{
if (!first)
{
SeparateObject();
}
first = false;
StatefulConvert(item.Key);
RelateObjectKeyValue();
StatefulConvert(item.Value);
}
EndObject();
return true;
}
return false;
}
private void ConvertObject(object obj)
{
StartObject();
if (obj is PSObject psObject)
{
bool first = true;
foreach (PSPropertyInfo property in psObject.Properties)
{
if (!first) { SeparateObject(); }
first = false;
WriteKey(property.Name);
RelateObjectKeyValue();
StatefulConvert(property.Value);
}
obj = psObject.BaseObject;
}
if (obj != null)
{
Type objectType = obj.GetType();
bool first = true;
foreach (FieldInfo field in objectType.GetFields(s_publicInstance))
{
if (!first) { SeparateObject(); }
first = false;
WriteKey(field.Name);
RelateObjectKeyValue();
StatefulConvert(field.GetValue(obj));
}
foreach (PropertyInfo property in objectType.GetProperties(s_publicInstance))
{
if (!first) { SeparateObject(); }
first = false;
WriteKey(property.Name);
RelateObjectKeyValue();
StatefulConvert(property.GetValue(obj));
}
}
EndObject();
}
private static bool IsKvpEnumerable(object obj)
{
Type type = obj.GetType();
foreach (Type interfaceType in type.GetInterfaces())
{
if (!interfaceType.IsGenericType
|| interfaceType.GetGenericTypeDefinition() != typeof(IEnumerable<>))
{
continue;
}
Type itemType = interfaceType.GetGenericArguments()[0];
if (itemType.IsGenericType
&& itemType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
{
return true;
}
}
return false;
}
}
public abstract class PsdConverter : PSObjectSerializer
{
protected virtual string StringDelimiter { get; } = "\"";
protected virtual string KeyStringDelimiter => StringDelimiter;
protected override void WriteString(string s)
{
StrBuffer.Append(StringDelimiter)
.Append(s)
.Append(StringDelimiter);
}
}
public class CompressedPsdConverter : PsdConverter
{
protected override void EndArray()
{
StrBuffer.Append(')');
}
protected override void EndObject()
{
StrBuffer.Append('}');
}
protected override void RelateObjectKeyValue()
{
StrBuffer.Append('=');
}
protected override void SeparateArray()
{
StrBuffer.Append(',');
}
protected override void SeparateObject()
{
StrBuffer.Append(';');
}
protected override void StartArray()
{
StrBuffer.Append("@(");
}
protected override void StartObject()
{
StrBuffer.Append("@{");
}
}
public class FormattedPsdConverter : PsdConverter
{
private int _currentIndentationDepth;
public FormattedPsdConverter()
{
_currentIndentationDepth = 0;
}
protected override void Reset()
{
base.Reset();
_currentIndentationDepth = 0;
}
private void AddIndent()
{
for (int i = 0; i < _currentIndentationDepth; i++)
{
StrBuffer.Append(" ");
}
}
private void AddNewline()
{
StrBuffer.AppendLine();
}
protected override void EndArray()
{
_currentIndentationDepth--;
AddNewline();
AddIndent();
StrBuffer.Append(')');
}
protected override void EndObject()
{
_currentIndentationDepth--;
AddNewline();
AddIndent();
StrBuffer.Append('}');
}
protected override void RelateObjectKeyValue()
{
StrBuffer.Append(" = ");
}
protected override void SeparateArray()
{
StrBuffer.Append(",");
AddNewline();
AddIndent();
}
protected override void SeparateObject()
{
AddNewline();
AddIndent();
}
protected override void StartArray()
{
StrBuffer.Append("@(");
_currentIndentationDepth++;
AddNewline();
AddIndent();
}
protected override void StartObject()
{
StrBuffer.Append("@{");
_currentIndentationDepth++;
AddNewline();
AddIndent();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment