Is it possible to customize the behavior as in json.Marshal/Unmarshal?
Fyria30 opened this issue · 5 comments
Hi! Thank you for the package!
UseCase:
I have nested structures and would like to ignore some fields on low level structures. Now to ignore some fields we need to write rules on top level every time. Like this:
type List[T any] struct {
Count int
Items []T
}
type Role struct {
Name string
Description string
}
type Client struct {
Roles List[Role]
Address string
}
func TestPatch(t *testing.T) {
var old, new Client
new.Roles.Items = append(new.Roles.Items, Role{Name: "test2", Description: "test2"})
new.Roles.Items = append(new.Roles.Items, Role{Name: "test", Description: "test"})
new.Roles.Count = 2
old.Roles.Items = append(old.Roles.Items, Role{Name: "test", Description: "test"})
old.Roles.Count = 1
patch, err := jsondiff.Compare(old, new, jsondiff.Equivalent() , jsondiff.Ignores("/Roles/Count"))
require.Nil(t, err)`
}
Would be cool to have something like this:
func (instance *List[T]) Compare(source interface{}, target interface{}, opts ...jsondiff.Option) (jsondiff.Patch, error) {
return jsondiff.Compare(source, target, jsondiff.Ignores("/Count"))
}
and then to get the same result as in first version by:
patch, err := jsondiff.Compare(old, new, jsondiff.Equivalent())
require.Nil(t, err)`
Hello,
If I understand correctly, what you are interested in is a jsondiff.Ignores
option that would support JSON pointers with wildcard support, such as (in your example): /Roles/*/Count
, to ignore the Count
field in every object of the Roles
array ?
Regarding the solution you describe, I see how this could eventually work, but I don't think that's the most elegant or obvious approach to your use case.
It also cool, but solves the problem only partly.
Let's assume we have the following structures and need to get diff. But List.Count should not be included in path because it depends on len of Items. And we do not need it, so we need to ignore it.
type List[T any] struct {
Count int
Items []T
}
type Role struct {
Name string
Description string
}
type Friend struct {
Name string
Description string
}
type Client struct {
Roles List[Role]
Address List[Friend]
}
func main (){
//...initialization now we have var a,b Client by somehow fullfilled so we need to find diff in roles and friends
patch, err := jsondiff.Compare(old, new, jsondiff.Equivalent() , jsondiff.Ignores("/Roles/Count"), jsondiff.Ignores("/Friends/Count"))
require.Nil(t, err)`
}
So we need manually write jsondiff.Ignores("/Roles/Count") , jsondiff.Ignores("/Friends/Count") every time instead to do it once in Compare Customize, and do it for every field with List type. Then we can imagine what we need to do for
type Event struct{
Client List[Clients]
Items List[Items]
}
patch, err := jsondiff.Compare(old, new, jsondiff.Equivalent() , jsondiff.Ignores("Clients/Roles/Count"), jsondiff.Ignores("Clients/Friends/Count"),jsondiff.Ignores("Items/.../Count") )
require.Nil(t, err)`
}
But would be cool to exclude it once in List method.
func (instance *List[T]) Compare(source interface{}, target interface{}, opts ...jsondiff.Option) (jsondiff.Patch, error) {
return jsondiff.Compare(source, target, jsondiff.Ignores("/Count"))
}
and then this give us count ignore in all List fields
patch, err := jsondiff.Compare(old, new, jsondiff.Equivalent()) )
require.Nil(t, err)`
}
Probably you can advise better solution for the case
Yep, I get the idea now, but unfortunately the package does not work directly with concrete types, instead it first marshal the input objects to JSON and unmarshal them again in an interface{}
to get a simpler representation based only on native Go types.
As such, there is no way to invoke a method like the example you gave (*List[T].Compare
) during the comparison, since it won't process a *List[T]
type but instead a map[string]interface{}
, and then a []interface{}
instead of *List[T].Items
.
Thank you for reply!
Alright, i got it.
I may have another solution for your use case using a custom implementation of the
MarshalJSON
method for your custom typeList[T]
.
Would be a way, but i need classic marhsal/unmarshal as well and can't change the implementation of this.
Anyway thank you for the replay and for your work on this project!
Would be a way, but i need classic marhsal/unmarshal as well and can't change the implementation of this.
Yep, this would require changes to alter the marshaling process to avoid having the Count
field in the final JSON representation.
I think a better Ignores
option supporting a JSON Path like syntax could be the solution to ignore nested fields at any level.