jeudi 15 juillet 2021

Is there a way to be recursive through different types of ProtoBuf fields?

I am trying to create a diff between protobuf messages that would work along all the IMessage objects (as far as I can tell the interface every protobuf implements) in our codebase.

To this end, I have made a generic method that would take any IMessage implementation that provides a parameterless constructor and would attempt to create a delta from such an example.

public class ProtocolDiffer<T> where T : IMessage, new()
    {
    public T Diff(T original, T replacement)
            {
                var delta = new T();
    
                foreach (var fieldDescriptor in replacement.Descriptor.Fields.InFieldNumberOrder())
                {
                    // Obtain the pair of values to compare
                    var originalFieldValue = fieldDescriptor.Accessor.GetValue(original);
                    var replacementFieldValue = fieldDescriptor.Accessor.GetValue(replacement);
                    Console.WriteLine(
                        $"Is the field {fieldDescriptor.Name.ToUpper()} equal between Original and Replacement? " +
                        $"{originalFieldValue.Equals(replacementFieldValue)}");
                    // These fields are equal, jump to the next pair
                    if (originalFieldValue.Equals(replacementFieldValue)) continue;
                    // They were not equal
                    // Is it a simple field?
                    if (fieldDescriptor.FieldType == FieldType.Message)
                    {
                        // Non-basic fields need to be evaluated instead of simply replaced
                        fieldDescriptor.Accessor.SetValue(delta,
                            Diff((T)(IMessage) fieldDescriptor.Accessor.GetValue(original),
                                (T)(IMessage) fieldDescriptor.Accessor.GetValue(replacement)));
                    }
                    else
                    {
                        fieldDescriptor.Accessor.SetValue(delta, fieldDescriptor.Accessor.GetValue(replacement));
                    }
                }
    
                return delta;
            }

The issue I am having is that the recursive mechanism fails since eventually the object being treated changes from the class we generated through protoc to a RepeatedField and the Diff will not be able to cast the objects.

I have tried using IMessage as the parameter to instanciate this Diff class but it does not provide a parameterless constructor. I am now considering creating a class that implements IMessage that would be able to be cast to by our protoc generated classes and RepeatedField but that will still take some time and I am unsure if I would even be able to provide the necessary casting in the first place.

Finally, this is all being done so that I can return the difference between two structures, including primitive fields and complex ones such as Lists, the idea being that the recursive mechanism would traverse the whole structure until it replaces every different field and then returns the structure with only those same fields set.

Is there a way to fix the casting issue so I can keep using the recursive mechanism?

Thanks in advance,





Aucun commentaire:

Enregistrer un commentaire