goccy / go-yaml

YAML support for the Go language

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Need Help with CustomMarshaler

cpunion opened this issue · comments

Hello,

I'm working with a data structure, which is intended to be flexible and may represent various user-defined structures. Here's an example structure:

type A struct {
  ID int
  X, Y int
  Dependent *A
  Children []*A
}

a := &A{
  ID: 1,
  X: 1,
  Y: 2,
}

b := &A{
  ID: 2,
  X: 3,
  Y: 4,
}

c := &A{
  ID: 3,
  X: 5,
  Y: 6,
  Children: []*A{
    a,
  },
  Dependent: &A{
    ID: 4,
    X: 7,
    Y: 8,
    Dependent: &A{
      ID: 5,
      X: 9,
      Y: 10,
    },
  },
}

I need to serialize this tree structure in a flattened structure, all references to objects only store their type/id. For example:

- type: a
  id: 3
  x: 5
  y: 6
  children:
    - type: a
      id: 1
  dependent:
    type: a
    id: 4
- type: a
  id: 4
  x: 7
  y: 8
  dependent:
    type: a
    id: 5
- type: a
  id: 5
  x: 9
  y: 10
- type: a
  id: 1
  x: 1
  y: 2
- type: a
  id: 2
  x: 3
  y: 4

I have been using the CustomMarshaler. My goal is to serialize the first-level objects normally, but for nested objects, return only type/id and queue them for delayed serialization. The challenge is to determine within the CustomMarshaler when an object is at the first level (i.e., the initial call of MarshalWithOptions to our CustomMarshaler) to serialize its simple fields (not sure how to accomplish this), while returning type/id for referenced fields.

Could anyone provide insights or approaches for handling this situation? Any help on how to effectively implement this type of serialization would be greatly appreciated.

Thank you!

Currently, I have replaced the types of Dependency and Children in my structure with the IComponent interface, and it seems to be working well. This change allows CustomMarshaler[ []IComponent ] and CustomMarshaler[IComponent] to handle the lazy serialization of the Children and Dependency fields. Meanwhile, the real type A is being marshaled using reflection. However, if all types are the actual type A, the outcome is either entirely returning type/id for each or serializing them all.

I need a CustomMarshaler like this:

func Serialize(a any) ([]byte, error) {
        encodeOption := yaml.CustomMarshaler(func(a *A) ([]byte, error) {
	        if a == currentRootObject {
		        // FIXME: this line will cause infinite recursion
		        return yaml.MarshalWithOptions(a, encodeOption)
	        } else {
		        return yaml.Marshal(&RefObject{
			        Type: "A",
			        Id: a.ID,
		        })
	        }
        })
        return yaml.MarshalWithOptions(a, encodeOption)
}