anchor.diff:  to_binary(DataObject) ->
       DataObject.
   
  -include(_Keys,_DataObject) -> ?NOT_SUPPORTED.
  -
  -exclude(_Keys,_DataObject) -> ?NOT_SUPPORTED.
  -
  -merge(_DataObject1,_DataObject2) -> ?NOT_SUPPORTED.
  -
  -diff(_DataObject1, _DataObject2) -> ?NOT_SUPPORTED.
  -    %% [{added,[{field,value}|_]},
  -    %%  {removed,[{fields,value}|_]},
  -    %%  {updated,[{field,value}|_]},
  -    %%  {replaced,[{field,value}|_]},
  -    %%  {changes,count}]
  -
  +%% @doc Builds a new data object with key/value pairs from provided data
  +%% object according if boolean filter function (fun(K,V) -> true | false) 
  +%% return true.
  +-spec filter(fun(),data_object()) -> data_object().
  +filter(Fun,DataObject) when is_list(DataObject) ->
  +    lists:foldl(fun({K,V},In) ->
  +                    case Fun(K,V) of
  +                        true -> [{K,V}|In];
  +                        false -> In
  +                    end end,
  +                [],
  +                DataObject);
  +filter(Fun,DataObject) when is_map(DataObject) ->
  +    map:fold(fun(K,V,In) ->
  +                    case Fun(K,V) of
  +                        true -> maps:put(K,V,In);
  +                        false -> In
  +                    end end,
  +             #{},
  +             DataObject);
  +filter(Fun,DataObject) ->
  +    ?TO_JSON(filter(Fun,?FROM_JSON(DataObject))).
  +    
  +    
  +
  +%% @doc Build data object with provided keys and associated values from
  +%% provided data object
  +-spec include(Keys,data_object()) -> data_object() when Keys :: [key() | Keys].
  +include(Keys,DataObject) when is_list(DataObject) -> 
  +    [{K,proplists:get_value(K,DataObject)} || K <- Keys];
  +include(Keys,DataObject) when is_map(DataObject) -> 
  +    lists:foldl(fun(K,MapIn) ->
  +                    maps:put(K,maps:get(K,DataObject),MapIn) end,
  +                #{},
  +                Keys);
  +include(Keys,DataObject) ->
  +    ?TO_JSON(include(Keys,?FROM_JSON(DataObject))).
  +
  +%% @doc Build data object with data from provided data object, excluding
  +%% the elements associated with provided keys
  +-spec exclude(Keys,data_object()) -> data_object() when Keys :: [key() | Keys].
  +exclude(Keys,DataObject) when is_list(DataObject) -> 
  +    [{K,V} || {K,V} <- DataObject, lists:member(K,Keys) =:= false];
  +exclude(Keys,DataObject) when is_map(DataObject) -> 
  +    map:fold(fun(K,V,MapIn) ->
  +                case lists:member(K,Keys) of
  +                    true -> MapIn;
  +                    false -> maps:put(K,V,MapIn)
  +                end end,
  +             #{},
  +             DataObject);
  +exclude(Keys,DataObject) ->
  +    ?TO_JSON(exclude(Keys,?FROM_JSON(DataObject))).
  +
  +%% @doc Merges two data objects into a data object. 
  +%% If two keys exists in both data objects  the value in the first data object
  +%% will be superseded by the value in the second data object, unless the value 
  +%% of the second data object is null.
  +-spec merge(data_object(),data_object()) -> data_object().
  +merge(DataObject1,DataObject2) when is_list(DataObject1), is_list(DataObject2) ->
  +    CleanObject2 = ?CL(DataObject2),
  +    Object2Keys = proplists:get_keys(CleanObject2),
  +    CleanObject2 ++ [{K,V} || {K,V} <- DataObject1, lists:member(K,Object2Keys) =:= false];
  +merge(DataObject1,DataObject2) when is_map(DataObject1), is_map(DataObject2) ->
  +    maps:merge(DataObject1,?CL(DataObject2));
  +merge(DataObject1,DataObject2) ->
  +    ?TO_JSON(merge(?FROM_JSON(DataObject1),?FROM_JSON(DataObject2))).
  +
  +%% @doc creates a diff data object between the first (old) and second (new) provided data
  +%% objects. null values are ignored. Resulting data object will have 5 root keys, each containg
  +%% corresponding key/value pairs. Outgoing format is the same as ingoing format.
  +%%
  +%% added : contains key/value pairs who were in data object 2 but not in data object 1
  +%% removed : contains key/value pairs who were in data object 1 but not in data objec 2
  +%% updated : containts key/value pairs who are not indentical between data object 1 and 2,
  +%%           with the values from data object 2
  +%% replaced : containts key/value pairs who are not indentical between data object 1 and 2,
  +%%            with the values from data object 1
  +-spec diff(data_object(), data_object()) -> diff_object().
  +diff(RawDataObject1, RawDataObject2) when is_list(RawDataObject1), is_list(RawDataObject2) ->
  +    DataObject1 = ?CL(RawDataObject1),
  +    DataObject2 = ?CL(RawDataObject2),
  +    Object1Keys = proplists:keys(DataObject1),
  +    Object2Keys = proplists:keys(DataObject2),
  +    CommonKeys = lists:filter(fun(K) -> lists:member(K,Object1Keys) end, Object2Keys),
  +
  +    UpdatedJob = fun(K,Acc) -> 
  +                    case {proplists:get_value(K,DataObject1),
  +                          proplists:get_value(K,DataObject2)} of
  +                          {__Same,__Same} -> Acc;
  +                          {__Old,__New}   -> [{K,__Old,__New} | Acc]
  +                    end end,
  +    UpdatedTuples = lists:foldl(UpdatedJob,
  +                                [],
  +                                CommonKeys),
  +
  +    Added = lists:filter(fun({K,_}) -> lists:member(K,Object1Keys) =:= false end, DataObject2),
  +    Removed = lists:filter(fun({K,_}) -> lists:member(K,Object2Keys) =:= false end, DataObject1),
  +    Updated = [{K,V} || {K,_,V} <- UpdatedTuples],
  +    Replaced = [{K,V} || {K,V,_} <- UpdatedTuples],
  +
  +    [{added,Added},
  +     {removed,Removed},
  +     {updated,Updated},
  +     {replaced,Replaced},
  +     {changes, length(Added) + length(Removed) + length(Updated)}];
  +diff(RawDataObject1, RawDataObject2) when is_map(RawDataObject1), is_map(RawDataObject2) ->
  +    DataObject1 = ?CL(RawDataObject1),
  +    DataObject2 = ?CL(RawDataObject2),
  +
  +    Object1Keys = maps:keys(DataObject1),
  +    Object2Keys = maps:keys(DataObject2),
  +    CommonKeys = lists:filter(fun(K) -> lists:member(K,Object1Keys) end, Object2Keys),
  +
  +    UpdatedJob = fun(K,Acc) -> 
  +                    case {maps:get(K,DataObject1),
  +                          maps:get(K,DataObject2)} of
  +                          {__Same,__Same} -> Acc;
  +                          {__Old,__New}   -> [{K,__Old,__New} | Acc]
  +                    end end,
  +    UpdatedTuples = lists:foldl(UpdatedJob,
  +                                [],
  +                                CommonKeys),
  +    Added = lists:foldl(fun(K,MapIn) -> 
  +                         case lists:member(K,Object1Keys) of
  +                                false -> maps:put(K,maps:get(K,DataObject2),MapIn);
  +                                true  -> MapIn
  +                         end end,
  +                        #{},
  +                        Object2Keys),
  +    Removed = lists:foldl(fun(K,MapIn) -> 
  +                           case lists:member(K,Object2Keys) of
  +                                  false -> maps:put(K,maps:get(K,DataObject1),MapIn);
  +                                  true  -> MapIn
  +                           end end,
  +                          #{},
  +                          Object1Keys),
  +    Updated = lists:foldl(fun({K,_,V},MapIn) -> maps:put(K,V,MapIn) end,
  +                          #{},
  +                          UpdatedTuples),
  +    Replaced = lists:foldl(fun({K,V,_},MapIn) -> maps:put(K,V,MapIn) end,
  +                           #{},
  +                           UpdatedTuples),
  +
  +    #{added => Added,
  +      removed => Removed,
  +      updated => Updated,
  +      replaced => Replaced,
  +      changes =>  maps:size(Added) + maps:size(Removed) + maps:size(Updated)};
  +diff(DataObject1, DataObject2) ->
  +    ?TO_JSON(diff(?FROM_JSON(DataObject1),?FROM_JSON(DataObject2))).
  +
  +% Added: keys in 2 who are not in 1
  +% Removed: keys in 1 who are not in 2
  +% Update: keys in 2 who are in 1 but with different values
  +% Replaced: keys in 1 who are in 2 but with different values
  +% Changes: count added + count removed + count replaced
   
   %% @doc Compare data objects for equality (null values are ignored)
   -spec equal(data_object(), data_object()) -> boolean.
   equal(DataObject1, DataObject2) when is_list(DataObject1), is_list(DataObject2) -> 
       lists:sort(?CL(DataObject1)) =:= lists:sort(?CL(DataObject2));
  -
   equal(DataObject1, DataObject2) when is_map(DataObject1), is_map(DataObject2) ->
       ?CL(DataObject1) =:= ?CL(DataObject2);
  -
   equal(DataObject1, DataObject2) ->
       equal(?FROM_JSON(DataObject1),?FROM_JSON(DataObject2)).
       
  
positive.code: -spec has_key(key(), data_object()) -> boolean().
  has_key(Key,DataObject) when is_list(DataObject) ->
      proplists:is_defined(Key,DataObject);
  
  has_key(Key,DataObject) when is_map(DataObject) ->
      maps:is_key(Key,DataObject);
  
  has_key(Key,DataObject) -> 
      has_key(Key,?FROM_JSON(DataObject)).
  
  
  keys(_DataObject) -> ?NOT_SUPPORTED.
  
  map(_Fun,_DataObject) -> ?NOT_SUPPORTED.
  
  new() -> ?NOT_SUPPORTED.
  
  new(_Type) -> ?NOT_SUPPORTED.
  
  put(_Key,_Value,_DataObject) -> ?NOT_SUPPORTED.
  
  remove(_Key,_DataObject) -> ?NOT_SUPPORTED.
  
  size(_DataObject) -> ?NOT_SUPPORTED.
  
  update(_Key,_Value,_DataObject) -> ?NOT_SUPPORTED.
  
  values(_DataObject) -> ?NOT_SUPPORTED.
  
  
  %% @doc Convert any format of DataObject to proplist
  -spec to_proplist(data_object()) -> list().
  to_proplist(DataObject) when is_list(DataObject) ->
      DataObject;
  
  to_proplist(DataObject) when is_map(DataObject) ->
      maps:to_list(DataObject);
  
  to_proplist(DataObject) ->
      to_proplist(?FROM_JSON(DataObject)).
  
  
negative.code: %% Type definitions
  %% ===================================================================
  -type data_object() :: list() | map() | binary().
  -type diff_object() :: list() | map() | binary().
  -type value() :: term().
  -type key() :: binary().
  
  