I have this method (consisting of three methods) that I need to use for looking through objects that are going to be serializes and logged as json objects.
a bit of intro (not necessary to read)
The initial problem was that I noticed that sometimes I would receive data in objects from external api's that would contain personal sensitive data i.e social security numbers. I would still like to log these objets though, I just want to sanitize the social sec numbers away, and I also would like to have some method that could this for object with any kind of internal structure, such that I could reuse it, and would'nt need to rewrite a new sanitization method for each log.
The problem
So now I wanted these methods that could look into objects of any type, and sanitize all the string fields, such that the strings would look the same, but for any string field with a social security number of the type 123456-7891, should be replaced with XXXXXX-XXXX.
public static void DoitNow(object obj)
{
Queue<PropertyInfo> properties = new Queue<PropertyInfo>(obj.GetType().GetProperties());
while (properties.Count != 0)
{
PropertyInfo property = properties.Dequeue();
if (property.GetValue(obj) is string)
{
string stringField = property.GetValue(obj).ToString();
String sanitizedField = sanitizeSensitiveString(stringField);
property.SetValue(obj, sanitizedField);
}
else if (property.GetValue(obj) is IList)
{
IList y = (IList)property.GetValue(obj);
if (y.Count != 0)
{
if (y[0] is string)
{
for (int i = 0; i < y.Count; i++)
y[i] = sanitizeSensitiveString((string)y[i]);
}
else if (IsNested(y[0]))
{
for (int i = 0; i < y.Count; i++)
{
DoitNow(y[i]);
}
}
}
}
else if (IsNested(property.GetValue(obj)))
{
var l = property.GetValue(obj);
DoitNow(l);
}
}
}
public static string sanitizeSensitiveString(string sensitiveString)
{
return Regex.Replace(sensitiveString, @"\d{6}-?\d{4}[0-9]*", " XXXXXX-XXXX ");
}
public static bool IsNested(object value)
{
Type t = value.GetType();
if (t.IsPrimitive || value is string)
{
return false;
}
FieldInfo[] fields = t.GetFields(BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.Instance);
if (fields.Any())
{
return true;
}
return false;
}
Now, the method DoitNow
is the core method here, it takes any object as a parameter, and uses projection to look through all the properties of it. If a field is a string field, we use the method sanitizeSensitiveString
to sanitize the string. If a property is itself a object (I use the method isNested
to determine this) I will call the method recursively on it. If a property is a List type, I look into the list and sanitize if it contains strings, and recurse if it contains complex objects.
I would like to get some tips on my IsNested
method, am I using projection right? In general am I using projection In a way that makes sense to access the fields and modify them?
Also, In order to be able to operate with many list types, I use the IList
interface, however this has led me to using for-loops in a way that I find very un-elegant, can I replace some of my loops with some functional type function calls? I also use if (list.count != 0) to test if a list is empty, I just find this plain stupid.
Any other optimizations are welcomed!
DoitNow
? Your parameter is namedobj
? \$\endgroup\$1234567891
so without the hyphen. \$\endgroup\$