How to get the names and values of all public and private fields from deserialized object?

How to get the names and values of all public and private fields from deserialized object?

Old forum URL: forums.lhotka.net/forums/t/10367.aspx


tesicg posted on Friday, May 13, 2011

Hi,

We work on project that uses Csla framework. I'm not much in Csla framework. Our current issue is how to get the names and values of all public and private fields from deserialized object?

The code looks as follows:

    private static object Deserialize(byte[] bin)
    {
      if (bin == null)
        return null;

      object obj = Csla.Serialization.Mobile.MobileFormatter.Deserialize(bin);
      if (obj == null)
        return null;
    }

    public static string DecodeToXml(string encodedText)
    {
      object obj = Deserialize(HCFB.UFO.Utils.CompressionUtility.Decompress(base64Decode(encodedText)));

      ...
    }

Thus, in DecodeToXml method we should write the code that should list the names and values of all private and public fields from deserialized object.

I've tried this:

//      objectType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic
//          | BindingFlags.Public);
      FieldInfo[] fields = objectType.GetFields();
      int count = fields.Count();
      foreach (FieldInfo fi in fields)
      {
          string fieldName = fi.Name;
          object fieldValue = fi.GetValue(obj);
      }

But, it doesn't work. That code returns all public fields only, but not their values. When I've tried it with the line that was commented out above, the number of returned fields is 0. I don't understand why.

We've tried this code as well:

      Type objectType = obj.GetType();
      MethodInfo oMethod =
          objectType.GetMethod(
          "GetProperty",
          BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public,
          null,
          new Type[] { typeof(Csla.Core.IPropertyInfo) },
          null);
      FieldInfo[] fields = objectType.GetFields(BindingFlags.Static | BindingFlags.Public);
      foreach (FieldInfo fi in fields)
      {
        if (fi.Name.EndsWith("Property"))
        {
          object oValue = oMethod.Invoke(obj, new object[] { fi.GetValue(obj) });
        }
      }

But, there's an exception:

System.Reflection.AmbiguousMatchException was unhandled by user code
  Message=Ambiguous match found.
  StackTrace:
       at System.DefaultBinder.SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
       at System.RuntimeType.GetMethodImpl(String name, BindingFlags bindingAttr, Binder binder, CallingConventions callConv, Type[] types, ParameterModifier[] modifiers)
       at System.Type.GetMethod(String name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
       at SilverlightApplication4.Transformer.DecodeToXml(String encodedText)
       at SilverlightApplication4.MainPage..ctor()
       at SilverlightApplication4.App.Application_Startup(Object sender, StartupEventArgs e)
       at MS.Internal.CoreInvokeHandler.InvokeEventHandler(Int32 typeIndex, Delegate handlerDelegate, Object sender, Object args)
       at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName)
  InnerException:

We really don't know what to do now. We need the code, which will help us to overcome this.

Could you help us, please?

Thank you in advance.

Goran

RockfordLhotka replied on Friday, May 13, 2011

Why do you want this information?

tesicg replied on Friday, May 13, 2011

That's the requirement for our project.

It's crucial now.

Is it possible to accomplish it?

RockfordLhotka replied on Friday, May 13, 2011

Anything is possible, but it would help to know why you need the information so I can help you identify the best solution.

tesicg replied on Friday, May 13, 2011

We work on test automation project where we test Silverlight client application. Our developers used Csla framework and some archiving library along with Silverlight code.

Our task is to write Silverlight DLL (actually, special wrapper) that should be used (imported) into HP LoadRunner. In that DLL we should write method that should decompress and deserialize some earlier compressed and serialized data. We have encoded long string as input. This line of code deserialize the data:

object obj = Csla.Serialization.Mobile.MobileFormatter.Deserialize(bin);

"object obj" can be one of many classes with its structure.

After that we should extract all fields (public and private) from that class and create XML file or similar as output.

That's the requirement of the project. I don't know why our client needs that. Actually, I work on different project, but my colleagues asked me to help them about this issue.

I hope it's clearer now.

I really need the code how to acomplish our goal.

RockfordLhotka replied on Friday, May 13, 2011

Your background information helps.

I would recommend allowing the object graph to completely deserialized, and to then "walk" through the object graph and use the field manager to pull out the values from each object.

This will require exposing the field manager through a public property or method on each business object. That is something you should NOT do in real code, but for a test load simulation it shouldn't cause problems.

Assuming each business object has a public FieldManager property, you can easily write a graph traversal algorithm that walks through the object graph to create an XML document with all field values from all objects in the graph.

tesicg replied on Monday, May 16, 2011

I understand what you wrote in general, but I wasn't not sure what to write. I'm not familiar with CSLA framework.

First of all I don't know how to get FieldManager property of the object I've got from deserialization:

object obj = Deserialize(HCFB.UFO.Utils.CompressionUtility.Decompress(base64Decode(encodedText)));

I assume you meant on "object obj" as business object that has public FieldManager property. If so, I can't see that property.

Is there any code sample that I could use?

RockfordLhotka replied on Monday, May 16, 2011

Unfortunately I am not in a postion where I can write people's code for them, and I don't have code that does this - the requirement has never come up in the past.

I am not suggesting you insert your code in the Decompress method - that occurs before the object graph is deserialized.

Are you trying to get at the XML data that is sent over the wire? Or are you trying to create an arbitrary XML document that represents the data in the business object graph?

If you are trying to get the XML that is sent over the wire, that'll be hard, because CSLA uses the BinaryXML format provided by .NET. So it isn't XML at all really.

What I suggested is to allow the business object graph to fully deserialize, and then pull the data out of the objects. That'd work if you are trying to create an arbitrary XML document that represents the data in the business object graph.

tesicg replied on Tuesday, May 17, 2011

Ok. I don't need whole solution to be written for me. I need the code that should I start with, because I can't see the way to get all public and private fields from deserialzed object.

Once again what we need. We got the object from this call:

object obj = Deserialize(HCFB.UFO.Utils.CompressionUtility.Decompress(base64Decode(encodedText)));

Object "obj" can be of different types. All I need is starting code how to get all public and private fields from deserialzed object. I've mentioned XML as output only to explain the requirement, but it's not the problem here. When I got all public and private fields, I'll use field names and values to create XML document where tags will be field names and values inside tags will be field values. That's all.

I've tried some things, but no succes. I don't know how to get all public and private fields from deserialzed object.

I know how to get all public and private fields by using classic reflection in classic .NET:

      Type objectType = person.GetType();

      FieldInfo[] fields = objectType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
      foreach (FieldInfo fi in fields)
      {
        string fieldName = fi.Name;
        if (fi.IsPrivate)
        {
          object fieldValue = objectType.InvokeMember(fi.Name, BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic, null, person, null);
        }
        else
        {
          object fieldValue = fi.GetValue(person);
        }
      }

But, it's not possible to get private fields in Silverlight in that way. That's by Microsoft documentation. And you said it was possible. I have never used CSLA.NET framework before. When you say "What I suggested is to allow the business object graph to fully deserialize, and then pull the data out of the objects" I understand that theoretically, but I don't know how to implement it using CSLA.NET framework. I only need the initial code how to do what you wrote. I've taken a short look into your book, but there's all about internal things, how to create business objects and so on. My problem is a bit the thing that should be done from outside. There's no code samples how to do such a thing. For example, about this "to allow the business object graph to fully deserialize", I'm stuck. How to do that? I only need code snippet how to start. I even don't know how to start.

Thank you for understanding.

RockfordLhotka replied on Tuesday, May 17, 2011

Put this code into each non-collection class:

    public Dictionary<stringobject> GetProperties()
    {
      var result = new Dictionary<stringobject>();
      foreach (var item in FieldManager.GetRegisteredProperties())
        if (!((item.RelationshipType & RelationshipTypes.Child) == RelationshipTypes.Child))
          result.Add(item.Name, ReadProperty(item));
      return result;
    }

    public List<object> GetChildProperties()
    {
      return FieldManager.GetChildren();
    }

Really you'd want to put this into custom base classes (create custom base classes for BusinessBase and ReadOnlyBase at a mimimum).

Then you can write code that can traverse the entire object graph, by calling these two methods to get the managed backing field values from each object, and to get its child objects.

There is no equivalent technique for getting entirely non-managed property values. In fact this is why the managed property concept was added to CSLA - to overcome the limitations of reflection in Silverlight. So as long as all your properties are registered this should work. If you are using CSLA 4.1 it should work with private backing fields too, as long as the property is registered.

tesicg replied on Wednesday, May 18, 2011

Thank you, but it seems we don't understand each other. I can't see how I can use the code you wrote. I'm not talking about business objects. I'm getting business objects through this method:

    private static object Deserialize(byte[] bin)
    {
      if (bin == null)
        return null;

      object obj = Csla.Serialization.Mobile.MobileFormatter.Deserialize(bin);
      if (obj == null)
        return null;

      return obj;
    }

And I did as follows - I've changed FieldManager property in BusinessBase class to be public instead of protected in CSLA source code and rebuilt the libraries. I've added newly compiled libraries to my project and wrote this code:

    public static string DecodeToXml(string encodedText)
    {
      // List that holds Xml tag names
      List<string> lstXmlTagNames = new List<string>();
      // List that holds Xml tag values
      List<string> lstXmlTagValues = new List<string>();

      // Get our object, but it works only if object is derived from BusinessBase class
      BusinessBase obj =
        (BusinessBase)Deserialize(HCFB.UFO.Utils.CompressionUtility.Decompress(base64Decode(encodedText)));

      // Get Xml root name
      string objName = obj.ToString();

      // Get FieldDataManager object, which is public now
      FieldDataManager fdm = obj.FieldManager;
      List<IPropertyInfo> piList = fdm.GetRegisteredProperties();
      for (int i = 0; i < piList.Count; i++)
      {
        string name = piList[ i ].Name;
        // Add new Xml tag name
        lstXmlTagNames.Add(name);

        IFieldData fi = fdm.GetFieldData(piList[ i ]);
        object val;
        // Add new Xml tag value
        if (fi != null)
        {
          val = fi.Value;
          if(val != null)
            lstXmlTagValues.Add(val.ToString());
          else
            lstXmlTagValues.Add(string.Empty);
        }
        else
        {
          lstXmlTagValues.Add(string.Empty);
        }
      }

      // Create Xml output
      string xml = CreateXml(objName, lstXmlTagNames, lstXmlTagValues);

      return xml;
    }

    private static string CreateXml(string rootName, List<string> lstXmlTagNames, List<string>  lstXmlTagValues)
    {
      XElement root = new XElement(rootName);
      for (int i = 0; i < lstXmlTagNames.Count; i++)
      {
        root.Add(new XElement(lstXmlTagNames[ i ], lstXmlTagValues[ i ]));
      }
      XElement xmlTree = new XElement(root);

      XDocument xmlDoc = new XDocument(xmlTree);
      string output = xmlDoc.ToString();

      return output;
    }

In case of having all public fields in business class, I've got the correct number of field names and values, but some of values are not in output xml. I don't know why. Also, when I'm debugging the application in VS2010, sometimes fields I got from this call:

        IFieldData fi = fdm.GetFieldData(piList[ i ]);

are null, and sometimes are not null. Those fields shouldn't be null, but sometimes they are. I don't know why.

My test application is Silverlight application and I'm getting business classes created by using CSLA.NET framework through this call:

      BusinessBase obj =
        (BusinessBase)Deserialize(HCFB.UFO.Utils.CompressionUtility.Decompress(base64Decode(encodedText)));

I don't know anything about the internal structure of these business classes.

FieldManager property is in BusinessBase class and I need it to be public in order to get all public and private fields. But, even if I made FieldManager property to be public it doesn't work as I expected.

I don't know how to do this in another way.

RockfordLhotka replied on Wednesday, May 18, 2011

I think we do understand each other. In the deserialize method you get back an object - in the field 'obj'. That object is, by definition, an IMobileObject because that's the only thing the MobileFormatter understands.

I'd recommend that you define an interface that exposes the field manager. Then build custom base classes that implement that interface to expose the field manager (there's no reason to alter the CSLA codebase to do this).

Then you code can just attempt to cast the object to this interface, so you aren't limited to BusinessBase.

It occurs to me though, that maybe you can use the IMobileObject interface to achieve your goal. I haven't thought through this - it is just an idea - but if you wrote an equivalent to MobileFormatter that generated your XML, your formatter could probably use the IMobileObject interface to get all the state of all the objects in a graph - just like MobileFormatter.

tesicg replied on Friday, May 20, 2011

Thank you Rockford. I've made some progress.

I have one more question. How to get the values in similar manner in case we get non-BusinessBase derived object? For instance, we have an object, which implements IMobileObject interface only? We got that object on regular basis using this call:

object obj = Csla.Serialization.Mobile.MobileFormatter.Deserialize(bin);

That object contains simple value such as: 12345.

RockfordLhotka replied on Friday, May 20, 2011

The answer is the same - that object, or its base class, needs to expose the field manager. Or it needs to expose some other property or method you can use to get its field value(s).

Without reflection, the object itself must participate in exposing its field values.

tesicg replied on Tuesday, May 24, 2011

Could you, please, explain this in more details:

"It occurs to me though, that maybe you can use the IMobileObject interface to achieve your goal. I haven't thought through this - it is just an idea - but if you wrote an equivalent to MobileFormatter that generated your XML, your formatter could probably use the IMobileObject interface to get all the state of all the objects in a graph - just like MobileFormatter." ?

And regarding this:

"I'd recommend that you define an interface that exposes the field manager. Then build custom base classes that implement that interface to expose the field manager (there's no reason to alter the CSLA codebase to do this)."

Do you mean on this:

  public interface IExposeFieldManager
  {
    FieldDataManager GetFieldDataManager();
  }

  public class ExposeFieldManager : UndoableBase, IExposeFieldManager
  {
    private FieldDataManager _fieldManager;

    public FieldDataManager GetFieldDataManager()
    {
      if (_fieldManager == null)
      {
        _fieldManager = new FieldDataManager(this.GetType());
        UndoableBase.ResetChildEditLevel(_fieldManager, this.EditLevel, this.BindingEdit);
      }
      return _fieldManager;
    }
  }

?

And where to out these code in that case:

    public Dictionary<string, object> GetProperties()
    {
      var result = new Dictionary<string, object>();
      foreach (var item in FieldManager.GetRegisteredProperties())
        if (!((item.RelationshipType & RelationshipTypes.Child) == RelationshipTypes.Child))
          result.Add(item.Name, ReadProperty(item));
      return result;
    }

    public List<object> GetChildProperties()
    {
      return FieldManager.GetChildren();
    }

?

But, I'm not sure how to read the value of the field because ReadProperty(item) is not accessible.

RockfordLhotka replied on Wednesday, May 25, 2011

Let me repeat, I believe I am helping you to create something that is really bad and unmaintainable. I make no promises that this won't all break in horrible ways with some future version of CSLA.

With that disclaimer, yes, your last post is correct, that is what I am talking about.

I suggest that your code that is pulling all the data from the objects should inherit from Csla.Server.ObjectFactory. The ObjectFactory base class is designed for building a data access layer, but you can misuse it for this purpose too.

The ObjectFactory base class has ReadProperty and LoadProperty methods that allow you to break encapsulation and access the fields of another object.

tesicg replied on Thursday, May 26, 2011

Thank you Rockford. I'm really aware of what you said about what I'm trying to do and in which way, but that's requirement.

Let me ask you last question about this issue. It refers to these your words:

"I'd recommend that you define an interface that exposes the field manager. Then build custom base classes that implement that interface to expose the field manager (there's no reason to alter the CSLA codebase to do this).

Then you code can just attempt to cast the object to this interface, so you aren't limited to BusinessBase."

I mean on last sentence: "Then you code can just attempt to cast the object to this interface, so you aren't limited to BusinessBase."

How can I do that in case I want to get all public and private fields from class:

namespace Csla.Silverlight
{
  public class PrimitiveCriteria : IMobileObject
  {
    private object _value;
    ...
  }
}

?

As you can see, there's one private field in PrimitiveCriteria class.

I'm not sure if I should alter PrimitiveCriteria class and if so, in which way. And I don't know how I can cast PrimitiveCriteria class to ExposeFieldManager class. The ExposeFieldManager class looks as follows:

  public interface IExposeFieldManager
  {
    FieldDataManager GetFieldDataManager();
    Dictionary<string, object> GetProperties();
    List<object> GetChildProperties();
  }

  public class ExposeFieldManager : ObjectFactory, IExposeFieldManager
  {
    private FieldDataManager _fieldManager;

    public FieldDataManager GetFieldDataManager()
    {
      if (_fieldManager == null)
        _fieldManager = new FieldDataManager(this.GetType());
      return _fieldManager;
    }

    public Dictionary<string, object> GetProperties()
    {
      var result = new Dictionary<string, object>();
      foreach (var item in GetFieldDataManager().GetRegisteredProperties())
        if (!((item.RelationshipType & RelationshipTypes.Child) == RelationshipTypes.Child))
          result.Add(item.Name, ReadProperty(item));
      return result;
    }

    public List<object> GetChildProperties()
    {
      return GetFieldDataManager().GetChildren();
    }
  }

Also, there's a problem with calling ReadProperty(item) method from ObjectFactory class because that method accepts 2 parameters.

And I have this class as well:

public class BankOfficeAccessRule : IAuthorizationRule

Can I get all public and private fields from this class as well?

RockfordLhotka replied on Thursday, May 26, 2011

If you are using private backing fields I can't help you. You will need to invent your own solution to that problem.

I invented the field manager and managed backing fields for CSLA to solve this problem. You can use managed backing fields, or invent your own solution.

tesicg replied on Friday, May 27, 2011

Thank you very much Rockford.

RockfordLhotka replied on Friday, May 13, 2011

Keep in mind that the MobileFormatter is used on Silverlight and WP7 - and only on .NET as part of the data portal when communicating with SL. All other .NET scenarios use the BinaryFormatter or NDCS, so any solution based around the MobileFormatter is limited.

Also keep in mind that the entire concept of serialization is something I view as a "black box" part of CSLA. So I feel entirely free to change that in the future, because it isn't part of the public API. Although I sometimes change public API elements, with those I worry about backward compatibility. For black box APIs like serialization I assume that if I break somebody's code it is their fault for interacting with non-public or highly advanced API elements.

In other words, you've been warned :)

You should start by reading the blog posts listed here:

Those blog posts are as in-depth as I go in terms of providing documentation for the MobileFormatter.

As you can see from http://www.lhotka.net/weblog/CSLALightObjectSerialization.aspx, the MobileFormatter uses the field manager and the IMobileObject interface to get the field values from an object, and that data is used to create a DTO object graph that can be serialized by the DataContractSerializer (DCS). The DCS uses a writer object to create the actual resulting byte stream - be that XML or binary XML. The default is binary XML.

I don't know for sure that you can get in the middle of this process without altering CSLA. The ability to intercept the data between the MobileFormatter and DCS isn't one of my design requirements.

But if you alter CSLA there's no doubt you can get in the middle of the process. In that case you could get full access to the DTO graph if you needed to.

Another option is to allow the deserialization to complete, and then override OnDeserialized in the root object. That way you'd be notified that the process was complete and you could use the field manager to access the object graph's data. This could be done without altering CSLA.

 

Copyright (c) Marimer LLC