JSON serialization

Posts   
 
    
HcD avatar
HcD
User
Posts: 214
Joined: 12-May-2005
# Posted on: 24-Aug-2015 10:19:58   

Hello,

using LLBLGen 4.2 and Frans' post @ https://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=21394&HighLight=1 , the json serialize/deserialize works like a charm, but I do have a few remaining issues :

  • When deserializing from JSON, the resulting entity is always "IsDirty=true, IsNew=true" and all fields have "IsChanged = true" Is there a hook to influence the deserializer so it can have some "logic" (eg. when a primary is filled in, the entity will not be IsNew. And most of the time, when fetching entities from the server, they will not be IsDirty and IsChanged either.

When posting back an entity from the client to the server, how could I collect just the "changes" and serialize those ? (so I don't post back the whole entity graph).

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 25-Aug-2015 00:28:43   

So you are using Newtonsoft's serializer, correct? Is this for WebAPI?

HcD avatar
HcD
User
Posts: 214
Joined: 12-May-2005
# Posted on: 25-Aug-2015 08:49:15   

Yup, it's for WebAPI.

But even in a UnitTest, where i convert to and back from JSON using these simple exension methods, the entities come out as "IsNew" and "IsDirty":

 public static class EntityExtensions
    {
        public static string ToJson<T>(this T entity) where T : IEntity2
        {
            string result = JsonConvert.SerializeObject(entity, JsonSerializer.Settings);
            return result;
        }

        public static T FromJson<T>(this string json) where T : IEntity2
        {

            var result = JsonConvert.DeserializeObject<T>(json, JsonSerializer.Settings);
            return result;
        }
}

The JsonSerializer settings are as described in Frans' blog:


    public static JsonSerializerSettings Settings = new JsonSerializerSettings()
        {
            
            MissingMemberHandling = MissingMemberHandling.Ignore,
            NullValueHandling = NullValueHandling.Include,
            DefaultValueHandling = DefaultValueHandling.Include,
            //settings for LLBLGen
            PreserveReferencesHandling = PreserveReferencesHandling.Objects,
            ContractResolver = new DefaultContractResolver() {IgnoreSerializableInterface = true, IgnoreSerializableAttribute = true}
        };

EDIT: As a "fix", I have made an extension method that I apply immediately after fetching an entity from the WebAPI :

        public static T MakeNonDirty<T>(this T entity) where T : IEntity2
        {
            ObjectGraphUtils oga = new ObjectGraphUtils();
            foreach (var e in oga.ProduceTopologyOrderedList((IEntity2)entity))
            {
                entity.IsNew = false;
                entity.IsDirty = false;
                entity.Fields.IsDirty = false;
                for (int field = 0; field < entity.Fields.Count; field++)
                    entity.Fields[field].IsChanged = false;
            }
            return entity;
        }

So getting it to the WPF client is "solved". But the real struggle now starts getting my edited entities back to the server (either via PUT or PATCH). I really only want to send my modified data, I'm looking into ObjectGraphUtils now to try and get the changes, but I'm not really finding a good solid solution.

Any ideas ?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 26-Aug-2015 18:02:52   

There's no change tracking in the serialized data, as the object created from the JSON doesn't have to be our type. So even though you deserialize it into an entity type, it won't have change tracking info, it's a plain json object. This is done to make sure the json data is usable in e.g. javascript without cryptic change tracking elements.

This also has the consequence that the data coming back is just plain data and deserialized back into objects has no notion whether it's new data or existing data that has been changed. When you use entity classes on both sides, use xml or binary serialization instead, as these have change tracking info embedded and changes made to entity objects on the client will be transferred back.

json serialization is outside the scope of the framework, also because the client can be another platform and/or code which doesn't do change tracking in its own objects and very likely if it does, it doesn't have compatible data storage for change tracking info in their own objects.

One way to overcome this is by deserializing the json into plain objects and then fetching the entities from the DB, and then setting the properties of these entities with the data received in the plain objects: if a field has changed value, it will be changed in the entity. If a plain object is new, it doesnt have an existing entity and a new one has to be created.

This is less automatic though. But could at least give better results.

Frans Bouma | Lead developer LLBLGen Pro
HcD avatar
HcD
User
Posts: 214
Joined: 12-May-2005
# Posted on: 26-Aug-2015 18:29:18   

Hey Frans,

Thanks for the explanation. We're a few days later now and my work progressed immensely.
I really want to use the json, to keep an open road for web clients, mobile clients, etc in the future.

But actually, LLBLGen is very well suited for this scenario, because of it's finegrained control over the Fields, with their IsChanged, DataType, CurrentValue properties. It makes it very simple to send back the modified data from client to server in a "patch" object. (inspired by http://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/ )

All in all, I'm very happy with the results, nice clean code, great performance, and fully "open standards". Once again, LLBLGen delivers.

It makes for very clean code, eg the Patch method on the WebAPI controller:

  [HttpPatch]
        public TestEntity Update(int id, IEnumerable<FieldPatch> patches)
        {
            TestEntity entity = new TestEntity (id).MakeNonDirty();
            foreach (var p in patches)
                p.Patch(entity);
            using (var adapter = new DataAccessAdapter())
            {
                adapter.SaveEntity(entity, true);
                return entity;
            }

Clean and superfast, because the database only gets hit to update the changed fields.

The FieldPatch class just has FieldName & Value properties, and a Patch()-helper method to set the value for named field on a Entity. Would have been much more difficult without the "Fields" collection on an Entity simple_smile

Also I already solved my issues with changes in an object graph, thanks to the ObjectGraphUtils class (are you aware that in the help, this class is mentioned a few times, saying that the full explanation of it is in the reference manual, but it's actually not there - at least I couldn't find it)

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 26-Aug-2015 21:52:30   

Well done.

(are you aware that in the help, this class is mentioned a few times, saying that the full explanation of it is in the reference manual, but it's actually not there - at least I couldn't find it)

The reference manual is downloaded from the download section "LLBLGenPro.RTL.ReferenceManual.chm". It has the definition (about, members...etc).

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 28-Aug-2015 17:26:42   

simple_smile

Small note: since v4 the Fields object contains its values internally in an array, not in the individual field objects, so an entity initially doesn't have field objects. Indexing into the Fields object will create them on the fly so you won't notice, but it's more efficient to use the methods on the Fields object to work with e.g. the IsChanged flag for a given field. See the reference manual on the IEntityFields2 interface for more info.

Frans Bouma | Lead developer LLBLGen Pro