Repository: gopcua/opcua
Pull request #766 (2025-02-03 02:54:13)

Change server node attributes to use data values directly.
By keeping the attribute values as DataValues instead of Variants it allows a user to set the timestamp, quality, or any other parameter of each attribute specifically instead of always returning a default.

The `SetAttribute` function already took a datavalue, so I don't expect there to be any real changes to user code - but now it will keep the value and not throw it away.  The big difference is users will have to be careful to set the correct flags on the datavalue where before it ignored these and set the values at the end.


Patch:
diff --git a/examples/server/node_server/server.go b/examples/server/node_server/server.go
index f40ee00..2169222 100644
--- a/examples/server/node_server/server.go
+++ b/examples/server/node_server/server.go
@@ -199,12 +199,12 @@ func main() {
 	// the parent node in the namespace if applicable.
 	var3 := server.NewNode(
 		ua.NewNumericNodeID(nodeNS.ID(), 12345), // you can use whatever node id you want here, whether it's numeric, string, guid, etc...
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("MyBrowseName")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: server.DataValueFromValue(attrs.BrowseName("MyBrowseName")),
+			ua.AttributeIDNodeClass:  server.DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(12.34) },
+		func() *ua.DataValue { return server.DataValueFromValue(12.34) },
 	)
 	nodeNS.AddNode(var3)
 	nns_obj.AddRef(var3, id.HasComponent, true)
@@ -219,7 +219,7 @@ func main() {
 			num++
 
 			// get the current value of the variable
-			last_value := var1.Value().Value().(float32)
+			last_value := var1.Value().Value.Value().(float32)
 			// and change it
 			last_value += 1
 
@@ -229,7 +229,7 @@ func main() {
 				SourceTimestamp: time.Now(),
 				EncodingMask:    ua.DataValueValue | ua.DataValueSourceTimestamp,
 			}
-			var1.SetAttribute(ua.AttributeIDValue, val)
+			var1.SetAttribute(ua.AttributeIDValue, &val)
 
 			// we also need to let the node namespace know that the value has changed so it can trigger the change notification
 			// and send the updated value to any subscribed clients.
@@ -246,7 +246,7 @@ func main() {
 		for {
 			changed_id := <-nodeNS.ExternalNotification
 			node := nodeNS.Node(changed_id)
-			value := node.Value().Value()
+			value := node.Value().Value.Value()
 			log.Printf("%s changed to %v", changed_id.String(), value)
 		}
 	}()
diff --git a/server/namespace_map.go b/server/namespace_map.go
index dcbabf7..875b152 100644
--- a/server/namespace_map.go
+++ b/server/namespace_map.go
@@ -159,12 +159,7 @@ func (ns *MapNamespace) Attribute(n *ua.NodeID, a ua.AttributeID) *ua.DataValue
 			}
 		}
 
-		return &ua.DataValue{
-			EncodingMask:    ua.DataValueServerTimestamp | ua.DataValueStatusCode | ua.DataValueValue,
-			ServerTimestamp: time.Now(),
-			Status:          ua.StatusOK,
-			Value:           attrval.Value,
-		}
+		return attrval.Value
 
 	}
 
@@ -385,13 +380,13 @@ func (ns *MapNamespace) Objects() *Node {
 	//reftype := ua.NewTwoByteNodeID(uint8(id.HasComponent)) // folder
 	n := NewNode(
 		oid,
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDNodeClass:     ua.MustVariant(int32(ua.NodeClassObject)),
-			ua.AttributeIDBrowseName:    ua.MustVariant(attrs.BrowseName(ns.name)),
-			ua.AttributeIDDisplayName:   ua.MustVariant(attrs.DisplayName(ns.name, ns.name)),
-			ua.AttributeIDDescription:   ua.MustVariant(uint32(ua.NodeClassObject)),
-			ua.AttributeIDDataType:      ua.MustVariant(typedef),
-			ua.AttributeIDEventNotifier: ua.MustVariant(int16(0)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDNodeClass:     DataValueFromValue(int32(ua.NodeClassObject)),
+			ua.AttributeIDBrowseName:    DataValueFromValue(attrs.BrowseName(ns.name)),
+			ua.AttributeIDDisplayName:   DataValueFromValue(attrs.DisplayName(ns.name, ns.name)),
+			ua.AttributeIDDescription:   DataValueFromValue(uint32(ua.NodeClassObject)),
+			ua.AttributeIDDataType:      DataValueFromValue(typedef),
+			ua.AttributeIDEventNotifier: DataValueFromValue(int16(0)),
 		},
 		[]*ua.ReferenceDescription{},
 		nil,
@@ -402,10 +397,10 @@ func (ns *MapNamespace) Objects() *Node {
 func (ns *MapNamespace) Root() *Node {
 	n := NewNode(
 		ua.NewNumericNodeID(ns.ID(), id.RootFolder),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDNodeClass:   ua.MustVariant(int32(ua.NodeClassObject)),
-			ua.AttributeIDBrowseName:  ua.MustVariant(attrs.BrowseName("Root")),
-			ua.AttributeIDDisplayName: ua.MustVariant(attrs.DisplayName("Root", "")),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDNodeClass:   DataValueFromValue(int32(ua.NodeClassObject)),
+			ua.AttributeIDBrowseName:  DataValueFromValue(attrs.BrowseName("Root")),
+			ua.AttributeIDDisplayName: DataValueFromValue(attrs.DisplayName("Root", "")),
 		},
 		[]*ua.ReferenceDescription{},
 		nil,
diff --git a/server/namespace_node.go b/server/namespace_node.go
index 102613d..9bc97d7 100644
--- a/server/namespace_node.go
+++ b/server/namespace_node.go
@@ -47,13 +47,13 @@ func NewNodeNameSpace(srv *Server, name string) *NodeNameSpace {
 	//reftype := ua.NewTwoByteNodeID(uint8(id.HasComponent)) // folder
 	objectsNode := NewNode(
 		oid,
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDNodeClass:     ua.MustVariant(uint32(ua.NodeClassObject)),
-			ua.AttributeIDBrowseName:    ua.MustVariant(attrs.BrowseName(ns.name)),
-			ua.AttributeIDDisplayName:   ua.MustVariant(attrs.DisplayName(ns.name, ns.name)),
-			ua.AttributeIDDescription:   ua.MustVariant(uint32(ua.NodeClassObject)),
-			ua.AttributeIDDataType:      ua.MustVariant(typedef),
-			ua.AttributeIDEventNotifier: ua.MustVariant(int16(0)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDNodeClass:     DataValueFromValue(uint32(ua.NodeClassObject)),
+			ua.AttributeIDBrowseName:    DataValueFromValue(attrs.BrowseName(ns.name)),
+			ua.AttributeIDDisplayName:   DataValueFromValue(attrs.DisplayName(ns.name, ns.name)),
+			ua.AttributeIDDescription:   DataValueFromValue(uint32(ua.NodeClassObject)),
+			ua.AttributeIDDataType:      DataValueFromValue(typedef),
+			ua.AttributeIDEventNotifier: DataValueFromValue(int16(0)),
 		},
 		[]*ua.ReferenceDescription{},
 		nil,
@@ -126,12 +126,12 @@ func (as *NodeNameSpace) Attribute(id *ua.NodeID, attr ua.AttributeID) *ua.DataV
 
 	switch attr {
 	case ua.AttributeIDNodeID:
-		a = &AttrValue{Value: ua.MustVariant(id)}
+		a = &AttrValue{Value: DataValueFromValue(id)}
 	case ua.AttributeIDEventNotifier:
 		// TODO: this is a hack to force the EventNotifier to false for everything.
 		// If at some point someone or something needs to use this, this will have to go away and be
 		// fixed properly.
-		a = &AttrValue{Value: ua.MustVariant(byte(0))}
+		a = &AttrValue{Value: DataValueFromValue(byte(0))}
 	case ua.AttributeIDNodeClass:
 		a, err = n.Attribute(attr)
 		if err != nil {
@@ -142,9 +142,9 @@ func (as *NodeNameSpace) Attribute(id *ua.NodeID, attr ua.AttributeID) *ua.DataV
 			}
 		}
 		// TODO: we need int32 instead of uint32 here.  this isn't the right place to fix it, but it is a bandaid
-		x, ok := a.Value.Value().(uint32)
+		x, ok := a.Value.Value.Value().(uint32)
 		if ok {
-			a.Value = ua.MustVariant(int32(x))
+			a.Value.Value = ua.MustVariant(int32(x))
 		}
 	default:
 		a, err = n.Attribute(attr)
@@ -157,12 +157,7 @@ func (as *NodeNameSpace) Attribute(id *ua.NodeID, attr ua.AttributeID) *ua.DataV
 			Status:          ua.StatusBadAttributeIDInvalid,
 		}
 	}
-	return &ua.DataValue{
-		EncodingMask:    ua.DataValueServerTimestamp | ua.DataValueStatusCode | ua.DataValueValue,
-		ServerTimestamp: time.Now(),
-		Status:          ua.StatusOK,
-		Value:           a.Value,
-	}
+	return a.Value
 }
 
 func (as *NodeNameSpace) Node(id *ua.NodeID) *Node {
@@ -258,12 +253,12 @@ func (as *NodeNameSpace) SetAttribute(id *ua.NodeID, attr ua.AttributeID, val *u
 
 	access, err := n.Attribute(ua.AttributeIDUserAccessLevel)
 	if err == nil {
-		x := access.Value.Value()
+		x := access.Value.Value.Value()
 		_ = x
 		return ua.StatusBadUserAccessDenied
 	}
 
-	err = n.SetAttribute(attr, *val)
+	err = n.SetAttribute(attr, val)
 	if err != nil {
 		return ua.StatusBadAttributeIDInvalid
 	}
diff --git a/server/node.go b/server/node.go
index 0aeb485..0c31216 100644
--- a/server/node.go
+++ b/server/node.go
@@ -12,21 +12,31 @@ import (
 	"github.com/gopcua/opcua/ua"
 )
 
-type Attributes map[ua.AttributeID]*ua.Variant
+type Attributes map[ua.AttributeID]*ua.DataValue
 
 type References []*ua.ReferenceDescription
 
-type ValueFunc func() *ua.Variant
+type ValueFunc func() *ua.DataValue
 
 type AttrValue struct {
-	Value           *ua.Variant
+	Value           *ua.DataValue
 	SourceTimestamp time.Time
 }
 
-func NewAttrValue(v *ua.Variant) *AttrValue {
+func NewAttrValue(v *ua.DataValue) *AttrValue {
 	return &AttrValue{Value: v}
 }
 
+func DataValueFromValue(val any) *ua.DataValue {
+
+	v := ua.MustVariant(val)
+	return &ua.DataValue{
+		EncodingMask:    ua.DataValueValue,
+		Value:           v,
+		SourceTimestamp: time.Now(),
+	}
+}
+
 type Node struct {
 	id   *ua.NodeID
 	attr Attributes
@@ -48,12 +58,12 @@ func NewFolderNode(nodeID *ua.NodeID, name string) *Node {
 	typedef := ua.NewNumericExpandedNodeID(0, id.ObjectsFolder)
 	n := NewNode(
 		nodeID,
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDNodeClass:     ua.MustVariant(uint32(ua.NodeClassObject)),
-			ua.AttributeIDBrowseName:    ua.MustVariant(attrs.BrowseName(name)),
-			ua.AttributeIDDisplayName:   ua.MustVariant(attrs.DisplayName(name, name)),
-			ua.AttributeIDDescription:   ua.MustVariant(ua.NodeClassObject),
-			ua.AttributeIDEventNotifier: ua.MustVariant(int16(0)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDNodeClass:     DataValueFromValue(uint32(ua.NodeClassObject)),
+			ua.AttributeIDBrowseName:    DataValueFromValue(attrs.BrowseName(name)),
+			ua.AttributeIDDisplayName:   DataValueFromValue(attrs.DisplayName(name, name)),
+			ua.AttributeIDDescription:   DataValueFromValue(ua.NodeClassObject),
+			ua.AttributeIDEventNotifier: DataValueFromValue(int16(0)),
 		},
 		[]*ua.ReferenceDescription{{
 			ReferenceTypeID: reftype,
@@ -71,22 +81,22 @@ func NewFolderNode(nodeID *ua.NodeID, name string) *Node {
 
 func NewVariableNode(nodeID *ua.NodeID, name string, value any) *Node {
 	//eoid := ua.NewNumericExpandedNodeID(nodeID.Namespace(), nodeID.IntID())
-	vf, ok := value.(func() *ua.Variant)
+	vf, ok := value.(func() *ua.DataValue)
 	if !ok {
 		typedef := ua.NewNumericExpandedNodeID(0, id.VariableNode)
 		n := NewNode(
 			nodeID,
-			map[ua.AttributeID]*ua.Variant{
-				ua.AttributeIDNodeClass:     ua.MustVariant(uint32(ua.NodeClassVariable)),
-				ua.AttributeIDBrowseName:    ua.MustVariant(attrs.BrowseName(name)),
-				ua.AttributeIDDisplayName:   ua.MustVariant(attrs.DisplayName(name, name)),
-				ua.AttributeIDDescription:   ua.MustVariant(uint32(ua.NodeClassVariable)),
-				ua.AttributeIDDataType:      ua.MustVariant(typedef),
-				ua.AttributeIDEventNotifier: ua.MustVariant(int16(0)),
+			map[ua.AttributeID]*ua.DataValue{
+				ua.AttributeIDNodeClass:     DataValueFromValue(uint32(ua.NodeClassVariable)),
+				ua.AttributeIDBrowseName:    DataValueFromValue(attrs.BrowseName(name)),
+				ua.AttributeIDDisplayName:   DataValueFromValue(attrs.DisplayName(name, name)),
+				ua.AttributeIDDescription:   DataValueFromValue(uint32(ua.NodeClassVariable)),
+				ua.AttributeIDDataType:      DataValueFromValue(typedef),
+				ua.AttributeIDEventNotifier: DataValueFromValue(int16(0)),
 			},
 			[]*ua.ReferenceDescription{},
-			func() *ua.Variant {
-				return ua.MustVariant(value)
+			func() *ua.DataValue {
+				return DataValueFromValue(value)
 			},
 		)
 		return n
@@ -94,13 +104,13 @@ func NewVariableNode(nodeID *ua.NodeID, name string, value any) *Node {
 	typedef := ua.NewNumericExpandedNodeID(0, id.VariableNode)
 	n := NewNode(
 		nodeID,
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDNodeClass:     ua.MustVariant(uint32(ua.NodeClassVariable)),
-			ua.AttributeIDBrowseName:    ua.MustVariant(attrs.BrowseName(name)),
-			ua.AttributeIDDisplayName:   ua.MustVariant(attrs.DisplayName(name, name)),
-			ua.AttributeIDDescription:   ua.MustVariant(uint32(ua.NodeClassVariable)),
-			ua.AttributeIDDataType:      ua.MustVariant(typedef),
-			ua.AttributeIDEventNotifier: ua.MustVariant(int16(0)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDNodeClass:     DataValueFromValue(uint32(ua.NodeClassVariable)),
+			ua.AttributeIDBrowseName:    DataValueFromValue(attrs.BrowseName(name)),
+			ua.AttributeIDDisplayName:   DataValueFromValue(attrs.DisplayName(name, name)),
+			ua.AttributeIDDescription:   DataValueFromValue(uint32(ua.NodeClassVariable)),
+			ua.AttributeIDDataType:      DataValueFromValue(typedef),
+			ua.AttributeIDEventNotifier: DataValueFromValue(int16(0)),
 		},
 		[]*ua.ReferenceDescription{},
 		vf,
@@ -133,7 +143,7 @@ func (n *Node) ID() *ua.NodeID {
 	return n.id
 }
 
-func (n *Node) Value() *ua.Variant {
+func (n *Node) Value() *ua.DataValue {
 	if n.val == nil {
 		return nil
 	}
@@ -156,41 +166,41 @@ func (n *Node) Attribute(id ua.AttributeID) (*AttrValue, error) {
 		return nil, ua.StatusBadAttributeIDInvalid
 	}
 }
-func (n *Node) SetAttribute(id ua.AttributeID, val ua.DataValue) error {
+func (n *Node) SetAttribute(id ua.AttributeID, val *ua.DataValue) error {
 	switch {
 	case id == ua.AttributeIDValue:
 
 		// TODO: probably need to do some type checking here.
 		// And some permissions tests
-		n.val = func() *ua.Variant {
-			return ua.MustVariant(val.Value.Value())
+		n.val = func() *ua.DataValue {
+			return val
 		}
 
 		return nil
 	default:
-		n.attr[id] = val.Value
+		n.attr[id] = val
 	}
 	return ua.StatusBadNodeAttributesInvalid
 }
 
 func (n *Node) BrowseName() *ua.QualifiedName {
 	v := n.attr[ua.AttributeIDBrowseName]
-	if v == nil || v.Value() == nil {
+	if v == nil || v.Value.Value() == nil {
 		return &ua.QualifiedName{}
 	}
-	return v.Value().(*ua.QualifiedName)
+	return v.Value.Value().(*ua.QualifiedName)
 }
 
 func (n *Node) SetBrowseName(s string) {
-	n.attr[ua.AttributeIDBrowseName] = ua.MustVariant(&ua.QualifiedName{Name: s})
+	n.attr[ua.AttributeIDBrowseName] = DataValueFromValue(&ua.QualifiedName{Name: s})
 }
 
 func (n *Node) DisplayName() *ua.LocalizedText {
 	v := n.attr[ua.AttributeIDDisplayName]
-	if v == nil || v.Value() == nil {
+	if v == nil || v.Value.Value() == nil {
 		return &ua.LocalizedText{}
 	}
-	val := v.Value().(*ua.LocalizedText)
+	val := v.Value.Value().(*ua.LocalizedText)
 	val.UpdateMask()
 	return val
 }
@@ -198,19 +208,19 @@ func (n *Node) DisplayName() *ua.LocalizedText {
 func (n *Node) SetDisplayName(text, locale string) {
 	lt := &ua.LocalizedText{Text: text, Locale: locale}
 	lt.UpdateMask()
-	n.attr[ua.AttributeIDDisplayName] = ua.MustVariant(lt)
+	n.attr[ua.AttributeIDDisplayName] = DataValueFromValue(lt)
 }
 
 func (n *Node) Description() *ua.LocalizedText {
 	v := n.attr[ua.AttributeIDDescription]
-	if v == nil || v.Value() == nil {
+	if v == nil || v.Value.Value() == nil {
 		return &ua.LocalizedText{}
 	}
-	return v.Value().(*ua.LocalizedText)
+	return v.Value.Value().(*ua.LocalizedText)
 }
 
 func (n *Node) SetDescription(text, locale string) {
-	n.attr[ua.AttributeIDDescription] = ua.MustVariant(&ua.LocalizedText{Text: text, Locale: locale})
+	n.attr[ua.AttributeIDDescription] = DataValueFromValue(&ua.LocalizedText{Text: text, Locale: locale})
 }
 
 func (n *Node) DataType() *ua.ExpandedNodeID {
@@ -219,7 +229,7 @@ func (n *Node) DataType() *ua.ExpandedNodeID {
 		return ua.NewTwoByteExpandedNodeID(0)
 	}
 	v := n.attr[ua.AttributeIDDataType]
-	if v == nil || v.Value() == nil {
+	if v == nil || v.Value.Value() == nil {
 		// if we have a type definition, return that?
 		for i := range n.refs {
 			r := n.refs[i]
@@ -232,21 +242,21 @@ func (n *Node) DataType() *ua.ExpandedNodeID {
 		}
 		return ua.NewTwoByteExpandedNodeID(0)
 	}
-	return v.Value().(*ua.ExpandedNodeID)
+	return v.Value.Value().(*ua.ExpandedNodeID)
 }
 
 func (n *Node) SetNodeClass(nc ua.NodeClass) {
-	n.attr[ua.AttributeIDNodeClass] = ua.MustVariant(uint32(nc))
+	n.attr[ua.AttributeIDNodeClass] = DataValueFromValue(uint32(nc))
 }
 
 func (n *Node) NodeClass() ua.NodeClass {
 	v := n.attr[ua.AttributeIDNodeClass]
-	if v == nil || v.Value() == nil {
+	if v == nil || v.Value.Value() == nil {
 		return ua.NodeClassObject
 	}
-	vi32, ok := v.Value().(int32)
+	vi32, ok := v.Value.Value().(int32)
 	if !ok {
-		vui32, ok := v.Value().(uint32)
+		vui32, ok := v.Value.Value().(uint32)
 		if !ok {
 			return ua.NodeClassObject
 		}
diff --git a/server/nodeset2_import.go b/server/nodeset2_import.go
index 56ebb31..b71ef37 100644
--- a/server/nodeset2_import.go
+++ b/server/nodeset2_import.go
@@ -49,25 +49,25 @@ func (srv *Server) nodesImportNodeSet(nodes *schema.UANodeSet) error {
 
 		nid := ua.MustParseNodeID(rt.NodeIdAttr)
 
-		var attrs Attributes = make(map[ua.AttributeID]*ua.Variant)
-		attrs[ua.AttributeIDAccessRestrictions] = ua.MustVariant(rt.AccessRestrictionsAttr)
-		attrs[ua.AttributeIDBrowseName] = ua.MustVariant(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: rt.BrowseNameAttr})
-		attrs[ua.AttributeIDIsAbstract] = ua.MustVariant(rt.IsAbstractAttr)
-		attrs[ua.AttributeIDUserWriteMask] = ua.MustVariant(rt.UserWriteMaskAttr)
-		attrs[ua.AttributeIDSymmetric] = ua.MustVariant(rt.SymmetricAttr)
-		attrs[ua.AttributeIDWriteMask] = ua.MustVariant(rt.WriteMaskAttr)
+		var attrs Attributes = make(map[ua.AttributeID]*ua.DataValue)
+		attrs[ua.AttributeIDAccessRestrictions] = DataValueFromValue(rt.AccessRestrictionsAttr)
+		attrs[ua.AttributeIDBrowseName] = DataValueFromValue(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: rt.BrowseNameAttr})
+		attrs[ua.AttributeIDIsAbstract] = DataValueFromValue(rt.IsAbstractAttr)
+		attrs[ua.AttributeIDUserWriteMask] = DataValueFromValue(rt.UserWriteMaskAttr)
+		attrs[ua.AttributeIDSymmetric] = DataValueFromValue(rt.SymmetricAttr)
+		attrs[ua.AttributeIDWriteMask] = DataValueFromValue(rt.WriteMaskAttr)
 		if len(rt.DisplayName) > 0 {
-			attrs[ua.AttributeIDDisplayName] = ua.MustVariant(ua.NewLocalizedText(rt.DisplayName[0].Value))
+			attrs[ua.AttributeIDDisplayName] = DataValueFromValue(ua.NewLocalizedText(rt.DisplayName[0].Value))
 		}
 		if len(rt.InverseName) > 0 {
-			attrs[ua.AttributeIDInverseName] = ua.MustVariant(ua.NewLocalizedText(rt.InverseName[0].Value))
+			attrs[ua.AttributeIDInverseName] = DataValueFromValue(ua.NewLocalizedText(rt.InverseName[0].Value))
 		} else {
-			attrs[ua.AttributeIDInverseName] = ua.MustVariant(ua.NewLocalizedText(""))
+			attrs[ua.AttributeIDInverseName] = DataValueFromValue(ua.NewLocalizedText(""))
 		}
 		if len(rt.Description) > 0 {
-			attrs[ua.AttributeIDDescription] = ua.MustVariant(ua.NewLocalizedText(rt.Description[0].Value))
+			attrs[ua.AttributeIDDescription] = DataValueFromValue(ua.NewLocalizedText(rt.Description[0].Value))
 		}
-		attrs[ua.AttributeIDNodeClass] = ua.MustVariant(uint32(ua.NodeClassReferenceType))
+		attrs[ua.AttributeIDNodeClass] = DataValueFromValue(uint32(ua.NodeClassReferenceType))
 
 		var refs References = make([]*ua.ReferenceDescription, 0)
 
@@ -88,19 +88,19 @@ func (srv *Server) nodesImportNodeSet(nodes *schema.UANodeSet) error {
 		dt := nodes.UADataType[i]
 		nid := ua.MustParseNodeID(dt.NodeIdAttr)
 
-		var attrs Attributes = make(map[ua.AttributeID]*ua.Variant)
-		attrs[ua.AttributeIDAccessRestrictions] = ua.MustVariant(dt.AccessRestrictionsAttr)
-		attrs[ua.AttributeIDBrowseName] = ua.MustVariant(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: dt.BrowseNameAttr})
-		attrs[ua.AttributeIDIsAbstract] = ua.MustVariant(dt.IsAbstractAttr)
-		attrs[ua.AttributeIDUserWriteMask] = ua.MustVariant(dt.UserWriteMaskAttr)
-		attrs[ua.AttributeIDWriteMask] = ua.MustVariant(dt.WriteMaskAttr)
+		var attrs Attributes = make(map[ua.AttributeID]*ua.DataValue)
+		attrs[ua.AttributeIDAccessRestrictions] = DataValueFromValue(dt.AccessRestrictionsAttr)
+		attrs[ua.AttributeIDBrowseName] = DataValueFromValue(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: dt.BrowseNameAttr})
+		attrs[ua.AttributeIDIsAbstract] = DataValueFromValue(dt.IsAbstractAttr)
+		attrs[ua.AttributeIDUserWriteMask] = DataValueFromValue(dt.UserWriteMaskAttr)
+		attrs[ua.AttributeIDWriteMask] = DataValueFromValue(dt.WriteMaskAttr)
 		if len(dt.DisplayName) > 0 {
-			attrs[ua.AttributeIDDisplayName] = ua.MustVariant(ua.NewLocalizedText(dt.DisplayName[0].Value))
+			attrs[ua.AttributeIDDisplayName] = DataValueFromValue(ua.NewLocalizedText(dt.DisplayName[0].Value))
 		}
 		if len(dt.Description) > 0 {
-			attrs[ua.AttributeIDDescription] = ua.MustVariant(ua.NewLocalizedText(dt.Description[0].Value))
+			attrs[ua.AttributeIDDescription] = DataValueFromValue(ua.NewLocalizedText(dt.Description[0].Value))
 		}
-		attrs[ua.AttributeIDNodeClass] = ua.MustVariant(uint32(ua.NodeClassDataType))
+		attrs[ua.AttributeIDNodeClass] = DataValueFromValue(uint32(ua.NodeClassDataType))
 
 		var refs References = make([]*ua.ReferenceDescription, 0)
 
@@ -121,19 +121,19 @@ func (srv *Server) nodesImportNodeSet(nodes *schema.UANodeSet) error {
 	for i := range nodes.UAObjectType {
 		ot := nodes.UAObjectType[i]
 		nid := ua.MustParseNodeID(ot.NodeIdAttr)
-		var attrs Attributes = make(map[ua.AttributeID]*ua.Variant)
-		attrs[ua.AttributeIDAccessRestrictions] = ua.MustVariant(ot.AccessRestrictionsAttr)
-		attrs[ua.AttributeIDBrowseName] = ua.MustVariant(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: ot.BrowseNameAttr})
-		attrs[ua.AttributeIDIsAbstract] = ua.MustVariant(ot.IsAbstractAttr)
-		attrs[ua.AttributeIDUserWriteMask] = ua.MustVariant(ot.UserWriteMaskAttr)
-		attrs[ua.AttributeIDWriteMask] = ua.MustVariant(ot.WriteMaskAttr)
+		var attrs Attributes = make(map[ua.AttributeID]*ua.DataValue)
+		attrs[ua.AttributeIDAccessRestrictions] = DataValueFromValue(ot.AccessRestrictionsAttr)
+		attrs[ua.AttributeIDBrowseName] = DataValueFromValue(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: ot.BrowseNameAttr})
+		attrs[ua.AttributeIDIsAbstract] = DataValueFromValue(ot.IsAbstractAttr)
+		attrs[ua.AttributeIDUserWriteMask] = DataValueFromValue(ot.UserWriteMaskAttr)
+		attrs[ua.AttributeIDWriteMask] = DataValueFromValue(ot.WriteMaskAttr)
 		if len(ot.DisplayName) > 0 {
-			attrs[ua.AttributeIDDisplayName] = ua.MustVariant(ua.NewLocalizedText(ot.DisplayName[0].Value))
+			attrs[ua.AttributeIDDisplayName] = DataValueFromValue(ua.NewLocalizedText(ot.DisplayName[0].Value))
 		}
 		if len(ot.Description) > 0 {
-			attrs[ua.AttributeIDDescription] = ua.MustVariant(ua.NewLocalizedText(ot.Description[0].Value))
+			attrs[ua.AttributeIDDescription] = DataValueFromValue(ua.NewLocalizedText(ot.Description[0].Value))
 		}
-		attrs[ua.AttributeIDNodeClass] = ua.MustVariant(uint32(ua.NodeClassObjectType))
+		attrs[ua.AttributeIDNodeClass] = DataValueFromValue(uint32(ua.NodeClassObjectType))
 
 		var refs References = make([]*ua.ReferenceDescription, 0)
 
@@ -153,18 +153,18 @@ func (srv *Server) nodesImportNodeSet(nodes *schema.UANodeSet) error {
 	for i := range nodes.UAVariableType {
 		ot := nodes.UAVariableType[i]
 		nid := ua.MustParseNodeID(ot.NodeIdAttr)
-		var attrs Attributes = make(map[ua.AttributeID]*ua.Variant)
-		attrs[ua.AttributeIDAccessRestrictions] = ua.MustVariant(ot.AccessRestrictionsAttr)
-		attrs[ua.AttributeIDBrowseName] = ua.MustVariant(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: ot.BrowseNameAttr})
-		attrs[ua.AttributeIDUserWriteMask] = ua.MustVariant(ot.UserWriteMaskAttr)
-		attrs[ua.AttributeIDWriteMask] = ua.MustVariant(ot.WriteMaskAttr)
+		var attrs Attributes = make(map[ua.AttributeID]*ua.DataValue)
+		attrs[ua.AttributeIDAccessRestrictions] = DataValueFromValue(ot.AccessRestrictionsAttr)
+		attrs[ua.AttributeIDBrowseName] = DataValueFromValue(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: ot.BrowseNameAttr})
+		attrs[ua.AttributeIDUserWriteMask] = DataValueFromValue(ot.UserWriteMaskAttr)
+		attrs[ua.AttributeIDWriteMask] = DataValueFromValue(ot.WriteMaskAttr)
 		if len(ot.DisplayName) > 0 {
-			attrs[ua.AttributeIDDisplayName] = ua.MustVariant(ua.NewLocalizedText(ot.DisplayName[0].Value))
+			attrs[ua.AttributeIDDisplayName] = DataValueFromValue(ua.NewLocalizedText(ot.DisplayName[0].Value))
 		}
 		if len(ot.Description) > 0 {
-			attrs[ua.AttributeIDDescription] = ua.MustVariant(ua.NewLocalizedText(ot.Description[0].Value))
+			attrs[ua.AttributeIDDescription] = DataValueFromValue(ua.NewLocalizedText(ot.Description[0].Value))
 		}
-		attrs[ua.AttributeIDNodeClass] = ua.MustVariant(uint32(ua.NodeClassVariableType))
+		attrs[ua.AttributeIDNodeClass] = DataValueFromValue(uint32(ua.NodeClassVariableType))
 
 		var refs References = make([]*ua.ReferenceDescription, 0)
 
@@ -184,18 +184,18 @@ func (srv *Server) nodesImportNodeSet(nodes *schema.UANodeSet) error {
 	for i := range nodes.UAVariable {
 		ot := nodes.UAVariable[i]
 		nid := ua.MustParseNodeID(ot.NodeIdAttr)
-		var attrs Attributes = make(map[ua.AttributeID]*ua.Variant)
-		attrs[ua.AttributeIDAccessRestrictions] = ua.MustVariant(ot.AccessRestrictionsAttr)
-		attrs[ua.AttributeIDBrowseName] = ua.MustVariant(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: ot.BrowseNameAttr})
-		attrs[ua.AttributeIDUserWriteMask] = ua.MustVariant(ot.UserWriteMaskAttr)
-		attrs[ua.AttributeIDWriteMask] = ua.MustVariant(ot.WriteMaskAttr)
+		var attrs Attributes = make(map[ua.AttributeID]*ua.DataValue)
+		attrs[ua.AttributeIDAccessRestrictions] = DataValueFromValue(ot.AccessRestrictionsAttr)
+		attrs[ua.AttributeIDBrowseName] = DataValueFromValue(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: ot.BrowseNameAttr})
+		attrs[ua.AttributeIDUserWriteMask] = DataValueFromValue(ot.UserWriteMaskAttr)
+		attrs[ua.AttributeIDWriteMask] = DataValueFromValue(ot.WriteMaskAttr)
 		if len(ot.DisplayName) > 0 {
-			attrs[ua.AttributeIDDisplayName] = ua.MustVariant(ua.NewLocalizedText(ot.DisplayName[0].Value))
+			attrs[ua.AttributeIDDisplayName] = DataValueFromValue(ua.NewLocalizedText(ot.DisplayName[0].Value))
 		}
 		if len(ot.Description) > 0 {
-			attrs[ua.AttributeIDDescription] = ua.MustVariant(ua.NewLocalizedText(ot.Description[0].Value))
+			attrs[ua.AttributeIDDescription] = DataValueFromValue(ua.NewLocalizedText(ot.Description[0].Value))
 		}
-		attrs[ua.AttributeIDNodeClass] = ua.MustVariant(uint32(ua.NodeClassVariable))
+		attrs[ua.AttributeIDNodeClass] = DataValueFromValue(uint32(ua.NodeClassVariable))
 
 		var refs References = make([]*ua.ReferenceDescription, 0)
 
@@ -215,18 +215,18 @@ func (srv *Server) nodesImportNodeSet(nodes *schema.UANodeSet) error {
 	for i := range nodes.UAMethod {
 		ot := nodes.UAMethod[i]
 		nid := ua.MustParseNodeID(ot.NodeIdAttr)
-		var attrs Attributes = make(map[ua.AttributeID]*ua.Variant)
-		attrs[ua.AttributeIDAccessRestrictions] = ua.MustVariant(ot.AccessRestrictionsAttr)
-		attrs[ua.AttributeIDBrowseName] = ua.MustVariant(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: ot.BrowseNameAttr})
-		attrs[ua.AttributeIDUserWriteMask] = ua.MustVariant(ot.UserWriteMaskAttr)
-		attrs[ua.AttributeIDWriteMask] = ua.MustVariant(ot.WriteMaskAttr)
+		var attrs Attributes = make(map[ua.AttributeID]*ua.DataValue)
+		attrs[ua.AttributeIDAccessRestrictions] = DataValueFromValue(ot.AccessRestrictionsAttr)
+		attrs[ua.AttributeIDBrowseName] = DataValueFromValue(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: ot.BrowseNameAttr})
+		attrs[ua.AttributeIDUserWriteMask] = DataValueFromValue(ot.UserWriteMaskAttr)
+		attrs[ua.AttributeIDWriteMask] = DataValueFromValue(ot.WriteMaskAttr)
 		if len(ot.DisplayName) > 0 {
-			attrs[ua.AttributeIDDisplayName] = ua.MustVariant(ua.NewLocalizedText(ot.DisplayName[0].Value))
+			attrs[ua.AttributeIDDisplayName] = DataValueFromValue(ua.NewLocalizedText(ot.DisplayName[0].Value))
 		}
 		if len(ot.Description) > 0 {
-			attrs[ua.AttributeIDDescription] = ua.MustVariant(ua.NewLocalizedText(ot.Description[0].Value))
+			attrs[ua.AttributeIDDescription] = DataValueFromValue(ua.NewLocalizedText(ot.Description[0].Value))
 		}
-		attrs[ua.AttributeIDNodeClass] = ua.MustVariant(uint32(ua.NodeClassMethod))
+		attrs[ua.AttributeIDNodeClass] = DataValueFromValue(uint32(ua.NodeClassMethod))
 
 		var refs References = make([]*ua.ReferenceDescription, 0)
 
@@ -249,19 +249,19 @@ func (srv *Server) nodesImportNodeSet(nodes *schema.UANodeSet) error {
 		if ot.NodeIdAttr == "i=85" {
 			log.Printf("doing objects.")
 		}
-		var attrs Attributes = make(map[ua.AttributeID]*ua.Variant)
-		attrs[ua.AttributeIDAccessRestrictions] = ua.MustVariant(ot.AccessRestrictionsAttr)
-		attrs[ua.AttributeIDBrowseName] = ua.MustVariant(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: ot.BrowseNameAttr})
-		attrs[ua.AttributeIDUserWriteMask] = ua.MustVariant(ot.UserWriteMaskAttr)
-		attrs[ua.AttributeIDWriteMask] = ua.MustVariant(ot.WriteMaskAttr)
+		var attrs Attributes = make(map[ua.AttributeID]*ua.DataValue)
+		attrs[ua.AttributeIDAccessRestrictions] = DataValueFromValue(ot.AccessRestrictionsAttr)
+		attrs[ua.AttributeIDBrowseName] = DataValueFromValue(&ua.QualifiedName{NamespaceIndex: nid.Namespace(), Name: ot.BrowseNameAttr})
+		attrs[ua.AttributeIDUserWriteMask] = DataValueFromValue(ot.UserWriteMaskAttr)
+		attrs[ua.AttributeIDWriteMask] = DataValueFromValue(ot.WriteMaskAttr)
 		if len(ot.DisplayName) > 0 {
-			attrs[ua.AttributeIDDisplayName] = ua.MustVariant(ua.NewLocalizedText(ot.DisplayName[0].Value))
+			attrs[ua.AttributeIDDisplayName] = DataValueFromValue(ua.NewLocalizedText(ot.DisplayName[0].Value))
 		}
 		if len(ot.Description) > 0 {
-			attrs[ua.AttributeIDDescription] = ua.MustVariant(ua.NewLocalizedText(ot.Description[0].Value))
+			attrs[ua.AttributeIDDescription] = DataValueFromValue(ua.NewLocalizedText(ot.Description[0].Value))
 		}
 
-		attrs[ua.AttributeIDNodeClass] = ua.MustVariant(uint32(ua.NodeClassObject))
+		attrs[ua.AttributeIDNodeClass] = DataValueFromValue(uint32(ua.NodeClassObject))
 
 		var refs References = make([]*ua.ReferenceDescription, 0)
 
@@ -315,7 +315,7 @@ func (srv *Server) refsImportNodeSet(nodes *schema.UANodeSet) error {
 		rt.NodeIdAttr = aliases[alias]
 		isSymmetricValue, err := refnode.Attribute(ua.AttributeIDSymmetric)
 		if err == nil {
-			rt.SymmetricAttr = isSymmetricValue.Value.Value().(bool)
+			rt.SymmetricAttr = isSymmetricValue.Value.Value.Value().(bool)
 		}
 
 		_, ok := reftypes[alias]
diff --git a/server/server_nodes.go b/server/server_nodes.go
index 77d0a23..899a330 100644
--- a/server/server_nodes.go
+++ b/server/server_nodes.go
@@ -11,30 +11,30 @@ import (
 func CurrentTimeNode() *Node {
 	return NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus_CurrentTime),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("CurrentTime")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("CurrentTime")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(time.Now()) },
+		func() *ua.DataValue { return DataValueFromValue(time.Now()) },
 	)
 }
 
 func NamespacesNode(s *Server) *Node {
 	return NewNode(
 		ua.NewNumericNodeID(0, id.Server_NamespaceArray),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("Namespaces")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassObject)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("Namespaces")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassObject)),
 		},
 		nil,
-		func() *ua.Variant {
+		func() *ua.DataValue {
 			n := s.Namespaces()
 			ns := make([]string, len(n))
 			for i := range ns {
 				ns[i] = n[i].Name()
 			}
-			return ua.MustVariant(ns)
+			return DataValueFromValue(ns)
 		},
 	)
 }
@@ -43,12 +43,12 @@ func ServerCapabilitiesNodes(s *Server) []*Node {
 	var nodes []*Node
 	nodes = append(nodes, NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerCapabilities_OperationLimits_MaxNodesPerRead),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("MaxNodesPerRead")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("MaxNodesPerRead")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(s.cfg.cap.OperationalLimits.MaxNodesPerRead) },
+		func() *ua.DataValue { return DataValueFromValue(s.cfg.cap.OperationalLimits.MaxNodesPerRead) },
 	))
 	return nodes
 }
@@ -56,10 +56,10 @@ func ServerCapabilitiesNodes(s *Server) []*Node {
 func RootNode() *Node {
 	return NewNode(
 		ua.NewNumericNodeID(0, id.RootFolder),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDNodeClass:  ua.MustVariant(attrs.NodeClass(ua.NodeClassObject)),
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("Root")),
-			ua.AttributeIDDataType:   ua.MustVariant(ua.NewNumericExpandedNodeID(0, id.DataTypesFolder)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDNodeClass:  DataValueFromValue(attrs.NodeClass(ua.NodeClassObject)),
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("Root")),
+			ua.AttributeIDDataType:   DataValueFromValue(ua.NewNumericExpandedNodeID(0, id.DataTypesFolder)),
 		},
 		nil,
 		nil,
@@ -105,129 +105,129 @@ func ServerStatusNodes(s *Server, ServerNode *Node) []*Node {
 
 	sStatus := NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("Status")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("Status")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(ua.NewExtensionObject(s.Status())) },
+		func() *ua.DataValue { return DataValueFromValue(ua.NewExtensionObject(s.Status())) },
 	)
 
 	sState := NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus_State),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("ServerStatus")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("ServerStatus")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(int32(s.Status().State)) },
+		func() *ua.DataValue { return DataValueFromValue(int32(s.Status().State)) },
 	)
 	mName := NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus_BuildInfo_ManufacturerName),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("ProductName")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("ProductName")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(s.cfg.manufacturerName) },
+		func() *ua.DataValue { return DataValueFromValue(s.cfg.manufacturerName) },
 	)
 	pName := NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus_BuildInfo_ProductName),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("ProductName")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("ProductName")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(s.cfg.productName) },
+		func() *ua.DataValue { return DataValueFromValue(s.cfg.productName) },
 	)
 
 	pURI := NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus_BuildInfo_ProductURI),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("ProductURI")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("ProductURI")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(s.cfg.applicationURI) },
+		func() *ua.DataValue { return DataValueFromValue(s.cfg.applicationURI) },
 	)
 
 	bInfo := NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus_BuildInfo),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("BuildInfo")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("BuildInfo")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant("") },
+		func() *ua.DataValue { return DataValueFromValue("") },
 	)
 	sVersion := NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus_BuildInfo_SoftwareVersion),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("SoftwareVersion")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("SoftwareVersion")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(s.cfg.softwareVersion) },
+		func() *ua.DataValue { return DataValueFromValue(s.cfg.softwareVersion) },
 	)
 
 	bNumber := NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus_BuildInfo_BuildNumber),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("BuildNumber")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("BuildNumber")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(s.cfg.softwareVersion) },
+		func() *ua.DataValue { return DataValueFromValue(s.cfg.softwareVersion) },
 	)
 
 	ts := time.Now()
 	bDate := NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus_BuildInfo_BuildDate),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("BuildDate")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("BuildDate")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(ts) },
+		func() *ua.DataValue { return DataValueFromValue(ts) },
 	)
 	timeStart := NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus_StartTime),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("StartTime")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("StartTime")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(ts) },
+		func() *ua.DataValue { return DataValueFromValue(ts) },
 	)
 	timeCurrent := NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus_CurrentTime),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("CurrentTime")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("CurrentTime")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(time.Now()) },
+		func() *ua.DataValue { return DataValueFromValue(time.Now()) },
 	)
 
 	//Server_ServerStatus_SecondsTillShutdown                                                                                                                               = 2992
 	//Server_ServerStatus_ShutdownReason                                                                                                                                    = 2993
 	sTillShutdown := NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus_SecondsTillShutdown),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("SecondsTillShutdown")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("SecondsTillShutdown")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(int32(0)) },
+		func() *ua.DataValue { return DataValueFromValue(int32(0)) },
 	)
 	sReason := NewNode(
 		ua.NewNumericNodeID(0, id.Server_ServerStatus_ShutdownReason),
-		map[ua.AttributeID]*ua.Variant{
-			ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("ShutdownReason")),
-			ua.AttributeIDNodeClass:  ua.MustVariant(uint32(ua.NodeClassVariable)),
+		map[ua.AttributeID]*ua.DataValue{
+			ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("ShutdownReason")),
+			ua.AttributeIDNodeClass:  DataValueFromValue(uint32(ua.NodeClassVariable)),
 		},
 		nil,
-		func() *ua.Variant { return ua.MustVariant(int32(0)) },
+		func() *ua.DataValue { return DataValueFromValue(int32(0)) },
 	)
 
 	nodes := []*Node{sState, mName, pName, pURI, sVersion, bNumber, bDate, timeStart, timeCurrent, bInfo, sTillShutdown, sReason}


Test patch:
diff --git a/tests/go/server.go b/tests/go/server.go
index 46cfb9d..a26aabb 100644
--- a/tests/go/server.go
+++ b/tests/go/server.go
@@ -57,13 +57,13 @@ func startServer() *server.Server {
 
 	// Create some nodes for it.
 	n := nodeNS.AddNewVariableStringNode("ro_bool", true)
-	n.SetAttribute(ua.AttributeIDUserAccessLevel, ua.DataValue{EncodingMask: ua.DataValueValue, Value: ua.MustVariant(uint32(1))})
+	n.SetAttribute(ua.AttributeIDUserAccessLevel, &ua.DataValue{EncodingMask: ua.DataValueValue, Value: ua.MustVariant(uint32(1))})
 	nns_obj.AddRef(n, id.HasComponent, true)
 	n = nodeNS.AddNewVariableStringNode("rw_bool", true)
 	nns_obj.AddRef(n, id.HasComponent, true)
 
 	n = nodeNS.AddNewVariableStringNode("ro_int32", int32(5))
-	n.SetAttribute(ua.AttributeIDUserAccessLevel, ua.DataValue{EncodingMask: ua.DataValueValue, Value: ua.MustVariant(uint32(1))})
+	n.SetAttribute(ua.AttributeIDUserAccessLevel, &ua.DataValue{EncodingMask: ua.DataValueValue, Value: ua.MustVariant(uint32(1))})
 	nns_obj.AddRef(n, id.HasComponent, true)
 	n = nodeNS.AddNewVariableStringNode("rw_int32", int32(5))
 	nns_obj.AddRef(n, id.HasComponent, true)