UnmarshalJSON unmarshals the node by extracting the nodeType and numChildren fields added by [NodeBase.MarshalJSON] and then updating the node to the correct type and creating the correct number of children. Note that this method can not update the type of the node if it has no parent; to load a roo
(b []byte)
| 50 | // again through the children of its parent. You do not need to call [UnmarshalRootJSON] |
| 51 | // or worry about pointers changing if this node is already of the correct type. |
| 52 | func (n *NodeBase) UnmarshalJSON(b []byte) error { |
| 53 | typeStart := bytes.Index(b, []byte(`":`)) + 3 |
| 54 | typeEnd := bytes.Index(b, []byte(`",`)) |
| 55 | typeName := string(b[typeStart:typeEnd]) |
| 56 | // we may end up with an extraneous quote / space at the start |
| 57 | typeName = strings.TrimPrefix(strings.TrimSpace(typeName), `"`) |
| 58 | typ := types.TypeByName(typeName) |
| 59 | if typ == nil { |
| 60 | return fmt.Errorf("tree.NodeBase.UnmarshalJSON: type %q not found", typeName) |
| 61 | } |
| 62 | |
| 63 | // if our type does not match, we must replace our This to make it match |
| 64 | if n.NodeType() != typ { |
| 65 | parent := n.Parent |
| 66 | index := n.IndexInParent() |
| 67 | if index >= 0 { |
| 68 | n.Delete() |
| 69 | n.This = NewOfType(typ) |
| 70 | parent.AsTree().InsertChild(n.This, index) |
| 71 | n = n.This.AsTree() // our NodeBase pointer is now different |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | // We must delete any existing children first. |
| 76 | n.DeleteChildren() |
| 77 | |
| 78 | remainder := b[typeEnd+2:] |
| 79 | numStart := bytes.Index(remainder, []byte(`"numChildren":`)) |
| 80 | if numStart >= 0 { // numChildren may not be specified if it is 0 |
| 81 | numStart += 14 // start of actual number bytes |
| 82 | numEnd := bytes.Index(remainder, []byte(`,`)) |
| 83 | numString := string(remainder[numStart:numEnd]) |
| 84 | // we may end up with extraneous space at the start |
| 85 | numString = strings.TrimSpace(numString) |
| 86 | numChildren, err := strconv.Atoi(numString) |
| 87 | if err != nil { |
| 88 | return err |
| 89 | } |
| 90 | // We make placeholder NodeBase children that will be replaced |
| 91 | // with children of the correct type during their UnmarshalJSON. |
| 92 | for range numChildren { |
| 93 | New[NodeBase](n) |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | uv := reflectx.UnderlyingPointer(reflect.ValueOf(n.This)) |
| 98 | rtyp := unmarshalTypeCache[typeName] |
| 99 | if rtyp == nil { |
| 100 | // We must create a new type that has the exact same fields as the original type |
| 101 | // so that we can unmarshal into it without having infinite recursion on the |
| 102 | // UnmarshalJSON method. This works because [reflect.StructOf] does not promote |
| 103 | // methods on embedded fields, meaning that the UnmarshalJSON method on the NodeBase |
| 104 | // is not carried over and thus is not called, avoiding infinite recursion. |
| 105 | uvt := uv.Type().Elem() |
| 106 | fields := make([]reflect.StructField, uvt.NumField()) |
| 107 | for i := range fields { |
| 108 | fields[i] = uvt.Field(i) |
| 109 | } |
no test coverage detected