Serialize Entity Framework objects into JSON
Asked Answered
S

8

47

It seems that serializing Entity Framework objects into JSON is not possible using either WCF's native DataContractJsonSerializer or ASP.NET's native JavaScript serializer. This is due to the reference counting issues both serializers reject. I have also tried Json.NET, which also fails specifically on a Reference Counting issue.


Edit: Json.NET can now serialize and deserialize Entity Framework entities.


My objects are Entity Framework objects, which are overloaded to perform additional business functionality (eg. authentication, etc.) and I do not want to decorate these classes with platform-specific attributes, etc. as I want to present a platform-agnostic API.

I've actually blogged about the individual steps I went though at https://blog.programx.co.uk/2009/03/18/wcf-json-serialization-woes-and-a-solution/

Have I missed something obvious?

Steeplebush answered 18/3, 2009 at 11:53 Comment(3)
Yes JSon.NET serialize but I would like to return IQueryable<entity> not json string! If I were return IQueryable<entity> I could utilize of OData.Lithiasis
Link on bloggingabout.net. Is brokenSuborn
@MichaelFreidgeim Yeah, I realised this when someone deleted another post. Which was nice. Seems that blog decided to delete my blog. Not happy. I can only apologise. I have taken the time to look back on Internet archives and republish at a different location.Steeplebush
J
78

The way I do this is by projecting the data I want to serialize into an anonymous type and serializing that. This ensures that only the information I actually want in the JSON is serialized, and I don't inadvertently serialize something further down the object graph. It looks like this:

var records = from entity in context.Entities
              select new 
              {
                  Prop1 = entity.Prop1,
                  Prop2 = entity.Prop2,
                  ChildProp = entity.Child.Prop
              }
return Json(records);

I find anonymous types just about ideal for this. The JSON, obviously, doesn't care what type was used to produce it. And anonymous types give you complete flexibility as to what properties and structure you put into the JSON.

Jerejereld answered 18/3, 2009 at 12:30 Comment(7)
Excellent solution. Is there a viable way to deserialize a javascript object back into an EF object?Kingdon
Samuel, the default model binder can generally cope with EF types. But I prefer to deserialize to an edit-specific model, then map to the EF type.Jerejereld
How would you do this when you have to return a list of entities?Histamine
@Prabhu, the code in my post does return a list. Have you tried it?Jerejereld
Thanks! I was really getting tired of making views into dto objects for this and cluttering up my graph.Scale
I believe my answer to the same question is better in most scenarios to projection. What I really dont want to do is to have to 1. Create almost identical DTO objects or 2. recreate Navigation or custom properties through projection as this answer does.. if you want to avoid that check my answer here: #4053661Yonne
FYI - if you have one-to-many relationships, you can use a lambda to select a group of properties inside the linq query - e.g. ListProps = entity.MyChildList.Select(c => new { c.Obj1.Prop1, c.Obj2.Prop1, c.Obj2.Prop2 } ).ToList()Dodson
P
17

Microsoft made an error in the way they made EF objects into data contracts. They included the base classes, and the back links.

Your best bet will be to create equivalent Data Transfer Object classes for each of the entities you want to return. These would include only the data, not the behavior, and not the EF-specific parts of an entity. You would also create methods to translate to and from your DTO classes.

Your services would then return the Data Transfer Objects.

Potentilla answered 18/3, 2009 at 12:16 Comment(3)
There's an option now to make serialization one-directional. It's possible that option didn't exist when you made this post. Just thought I'd add it in case others come across this in the future.Poulson
@Yuck: add a link to info on this feature, please.Potentilla
As far as I know EF doesn't have such a setting. This is for Linq-to-SQL only.Wimsatt
G
4

Based off of @Craig Stuntz answer and similar to a DTO, for my solution I have created a partial class of the model (in a separate file) and a return object method with how I want it using only the properties that will be needed.

namespace TestApplication.Models
{
    public partial class Employee
    {
        public object ToObject()
        {
            return new
            {
                 EmployeeID = EmployeeID,
                 Name = Name,
                 Username = Username,
                 Office = Office,
                 PhoneNumber = PhoneNumber,
                 EmailAddress = EmailAddress,
                 Title = Title,
                 Department = Department,
                 Manager = Manager
            };
        }
    }
}

And then I call it simply in my return:

var employee = dbCtx.Employees.Where(x => x.Name == usersName).Single();
return employee.ToObject();

I think the accepted answer is more quick and easy, I just use my method to keep all of my returns consistent and DRY.

Grind answered 10/5, 2016 at 17:50 Comment(0)
J
2

My solution was to simply remove the parent reference on my child entities.

So in my model, I selected the relationship and changed the Parent reference to be Internal rather than Public.

May not be an ideal solution for all, but worked for me.

Jane answered 23/2, 2012 at 12:5 Comment(0)
N
1

One more solution if you want to have better code consistency is to use JavaScriptConverter which will handle circular reference dependencies and will not serialize such references.

I've blogged about here:

http://hellowebapps.com/2010-09-26/producing-json-from-entity-framework-4-0-generated-classes/

Nun answered 26/9, 2010 at 14:53 Comment(3)
I agree Mehal. I extended your example to handle some other cases in my answer here #4053661Yonne
The link is brokenSuborn
Oh, I'll try to fix it with a new oneNun
J
1

FYI I found an alternative solution

You can set the parent relationship as private so then the properties are not exposed during the translation removing the infinite property loop

Jane answered 28/5, 2012 at 10:10 Comment(0)
C
1

I battled with this problem for days,

Solution. Inside your edmx window. - right click and add code generation item - Select Code tab - select EF 4x.POCOC Entity Generator

If you don't see it, then you will have to install it with nuget, search EF.

The Entity generator will generate all you complex type and entity object into simple classes to serialize into json.

Cohleen answered 24/7, 2012 at 13:35 Comment(0)
H
1

I solved it by getting only object types from System namespace, and then convert them to Dictionary and then add them to list. Works good for me :)

It looks complicated, but this was the only generic solution that worked for me... I'm using this logic for a helper I'm making, so it's for a special use where I need to be able to intercept every object type in entity object, maybe someone could adapt it to his use.

List<Dictionary<string, string>> outputData = new List<Dictionary<string, string>>();

// convert all items to objects
var data = Data.ToArray().Cast<object>().ToArray();

// get info about objects; and get only those we need
// this will remove circular references and other stuff we don't need
PropertyInfo[] objInfos = data[0].GetType().GetProperties();
foreach (PropertyInfo info in objInfos) {
    switch (info.PropertyType.Namespace)
    { 
          // all types that are in "System" namespace should be OK
          case "System":
              propeties.Add(info.Name);
              break;
     }
}
Dictionary<string, string> rowsData = null;
foreach (object obj in data) {
     rowsData = new Dictionary<string, string>();
     Type objType = obj.GetType();
     foreach (string propertyName in propeties)
     {
//if You don't need to intercept every object type You could just call .ToString(), and remove other code
         PropertyInfo info = objType.GetProperty(propertyName);
         switch(info.PropertyType.FullName)
         {
               case "System.String":
                    var colData = info.GetValue(obj, null);
                    rowsData.Add(propertyName, colData != null ? colData.ToString() : String.Empty);
                    break;
//here You can add more variable types if you need so (like int and so on...)
           }
      }

      outputData .Add(rowsData); // add a new row
}

"outputData " is safe for JSON encode... Hope someone will find this solution helpful. It was fun writing it :)

Heer answered 3/1, 2014 at 13:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.