Created
December 9, 2019 21:18
-
-
Save rjmholt/dfb403556753f17fedd3075edd91459f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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