Simplifying these methods for iterating through object and modifying string fields 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&lt;PropertyInfo&gt; properties = new Queue&lt;PropertyInfo&gt;(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 &lt; y.Count; i++)
                        y[i] = sanitizeSensitiveString((string)y[i]);
                }
                else if (IsNested(y[0]))
                {
                    for (int i = 0; i &lt; 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, @&quot;\d{6}-?\d{4}[0-9]*&quot;, &quot; XXXXXX-XXXX &quot;);
}

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!
As always simplicity is king. In my opinion you have done a lots of extra effort which might not be necessary.
Let's look at the problem from 10 000 feet high:

You receive an arbitrary object as input
You need to produce a string output where the SN data are masked out

What if you first serialize the data and then mask it?
public static string MaskOut(object toBeMaskedObject,
    string regexPattern = @&quot;\d{6}-?\d{4}[0-9]*&quot;,
    string mask = &quot; XXXXXX-XXXX &quot;)
{
    var serializedData = JsonConvert.SerializeObject(toBeMaskedObject);
    return Regex.Replace(serializedData, regexPattern, mask);
}

And that's it.
I've used the following data structures for testing:
var simpleWithoutSN = new
{
    Desc = &quot;123&quot;,
    Id = 1
};

var simpleWithSN = new
{
    SN = &quot;123456-7891&quot;,
    Id = 1 
};

var nestedWithSN = new
{
    Id = 1,
    Items = new []
    {
        new { SN  = &quot;123456-7891&quot;,},
        new { SN  = &quot;123456-7892&quot;,},
    }
    
};

Console.WriteLine(MaskOut(simpleWithoutSN));
Console.WriteLine(MaskOut(simpleWithSN));
Console.WriteLine(MaskOut(nestedWithSN));

The output will be:
{&quot;Desc&quot;:&quot;123&quot;,&quot;Id&quot;:1}
{&quot;SN&quot;:&quot; XXXXXX-XXXX &quot;,&quot;Id&quot;:1}
{&quot;Id&quot;:1,&quot;Items&quot;:[{&quot;SN&quot;:&quot; XXXXXX-XXXX &quot;},{&quot;SN&quot;:&quot; XXXXXX-XXXX &quot;}]}