.net JSON or XML serialization and object creation with included field types

I'm trying to map a dynamic DTO to JSON or XML, but to get information about the field types. So I'm wondering if this is possible from an object that looks like this:

public class AddressDto
    {
        public string Street { get; set; }
    }
public class UserInfoDto
    {
        public string UserName { get; set; }            
        public int Age { get; set; }
        public AddressDto Address { get; set; }
    }

      

To get something like this in JSON (or XML):

{
    "fieldType": "UserInfoDto"
    "objectValue":
    {
        {
            "fieldType": "string",
            "fieldName": "UserName",
            "fieldValue": "John Doe",
        },
        {
            "fieldType": "integer",
            "fieldName": "Age",
            "fieldValue": "27",
        },
        {
            "fieldType": "AddressDto",
            "fieldName": "Address",
            "fieldValue": {
                "fieldType": "string",
                "fieldName": "Street",
                "fieldValue": "Lorem Ipsum"
            }
        }
    }
}

      

... and vice versa.

+3


source to share


1 answer


Yes it is possible, but you need to do some work to instruct the serializer how to format the output string. If you want to stick with the built-in .NET serializers, you can achieve this using the class System.Runtime.Serialization.Json.DataContractJsonSerializer

.

1- Create MetadataObject class as a wrapper object for the output

Define the following class and tag it [DataContract]

so it can be serialized:

[DataContract]
public class MetadataObject
{
    [DataMember(Name = "fieldType")]
    public string FieldType { get; set; }

    [DataMember(Name = "fieldName")]
    public string FieldName { get; set; }

    [DataMember(Name = "fieldValue")]
    public object FieldValue { get; set; }
}

      

2- Tell the serializer how to serialize the parent object (UserInfoDto)

To do this, you need your object to UserInfoDto

implement an interface ISerializable

(more specifically, a method GetObjectData()

) and mark it as [Serializable]

. You also need to include any custom types that this class will include. In this case, it will be the type AddressDto

as well List<MedataObject>

, which is created by the method GetObjectData()

. The last class looks like this:

[KnownType(typeof(AddressDto))]
[KnownType(typeof(List<MetadataObject>))]
[Serializable]
public class UserInfoDto : ISerializable
{
    public string UserName { get; set; }

    public int Age { get; set; }

    public AddressDto Address { get; set; }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        var nodes = this.GetType().GetProperties().Select(property =>
        {
            return new MetadataObject
            {
                FieldType = property.PropertyType.Name,
                FieldName = property.Name,
                FieldValue = property.GetValue(this, null)
            };
        }).ToList();

        info.AddValue("fieldType", this.GetType().Name);
        info.AddValue("objectValue", nodes);
    }
}

      

Note that the method GetObjectData()

uses reflection and creates a class MetadataObject

for each property. The beauty is that it makes the code more generic. So if you decide that you need more classes like this UserInfoDto

, you can put this logic in a base class and inherit other classes from it.

3. Tell the serializer how to serialize the child object (AddressDto)



Same as UserInfoDto

above, implement the child class ISerializable

and mark it as [Serializable]

:

[Serializable]
public class AddressDto : ISerializable
{
    public string Street { get; set; }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        foreach (var property in this.GetType().GetProperties())
        {
            info.AddValue("fieldType", property.PropertyType.Name);
            info.AddValue("fieldName", property.Name);
            info.AddValue("fieldValue", property.GetValue(this, null));
        }
    }
}

      

4 Put it all together

Finally, define your serializer like this:

DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(UserInfoDto));

var data = new UserInfoDto { Age = 30, UserName = "John" };
data.Address = new AddressDto { Street = "123 ABC" };

using (MemoryStream stream = new MemoryStream())
{
    using (StreamReader reader = new StreamReader(stream))
    {
        serializer.WriteObject(stream, data);
        stream.Position = 0;

        var output = reader.ReadToEnd();
    }
}

      

When you run it, output

it looks like this:

{  
   "fieldType":"UserInfoDto",
   "objectValue":[  
      {  
         "__type":"MetadataObject:#StackOverflow.Console",
         "fieldName":"UserName",
         "fieldType":"String",
         "fieldValue":"John"
      },
      {  
         "__type":"MetadataObject:#StackOverflow.Console",
         "fieldName":"Age",
         "fieldType":"Int32",
         "fieldValue":30
      },
      {  
         "__type":"MetadataObject:#StackOverflow.Console",
         "fieldName":"Address",
         "fieldType":"AddressDto",
         "fieldValue":{  
            "__type":"AddressDto:#StackOverflow.Console",
            "fieldType":"String",
            "fieldName":"Street",
            "fieldValue":"123 ABC"
         }
      }
   ]
}

      

Note that the properties are __type

automatically generated by the serializer. If you are using .NET 4.5 you can try the following so that it is not part of the output (you will most likely need them if the string needs to be deserialized back to an object)

+1


source







All Articles