diff --git a/ld/node.go b/ld/node.go index 51fbc94..5eb356e 100644 --- a/ld/node.go +++ b/ld/node.go @@ -189,34 +189,39 @@ func RdfToObject(n Node, useNativeTypes bool) (map[string]interface{}, error) { value := literal.Value if useNativeTypes { // use native datatypes for certain xsd types - if datatype == XSDString { - // don't add xsd:string - } else if datatype == XSDBoolean { - if value == "true" { + switch datatype { + case XSDString: + // prevent default case from matching, i.e. prevent adding @type for xsd:string + case XSDBoolean: + switch value { + case "true": rval["@value"] = true - } else if value == "false" { + case "false": rval["@value"] = false - } else { - // Else do not replace the value, and add the - // boolean type in + default: + // do not replace the value but add the boolean type rval["@type"] = datatype } - } else if (datatype == XSDInteger && patternInteger.MatchString(value)) /* http://www.w3.org/TR/xmlschema11-2/#integer */ || - (datatype == XSDDouble && patternDouble.MatchString(value)) /* http://www.w3.org/TR/xmlschema11-2/#nt-doubleRep */ { - d, _ := strconv.ParseFloat(value, 64) - if !math.IsNaN(d) && !math.IsInf(d, 0) { - if datatype == XSDInteger { - i := int64(d) - if fmt.Sprintf("%d", i) == value { - rval["@value"] = i - } - } else if datatype == XSDDouble { - rval["@value"] = d - } else { + case XSDInteger: // http://www.w3.org/TR/xmlschema11-2/#integer + if patternInteger.MatchString(value) { + i, err := strconv.ParseInt(value, 10, 64) + if err != nil { return nil, NewJsonLdError(ParseError, nil) } + rval["@value"] = i } - } else { + case XSDDouble, XSDFloat: // http://www.w3.org/TR/xmlschema11-2/#nt-doubleRep + if patternDouble.MatchString(value) { + d, err := strconv.ParseFloat(value, 64) + if err != nil { + return nil, NewJsonLdError(ParseError, nil) + } + // ParseFloat successfully parses strings like "NaN", "Inf", and "-Inf" without returning an error + if !math.IsNaN(d) && !math.IsInf(d, 0) { + rval["@value"] = d + } + } + default: // do not add xsd:string type rval["@type"] = datatype } diff --git a/ld/node_test.go b/ld/node_test.go new file mode 100644 index 0000000..c35b9ca --- /dev/null +++ b/ld/node_test.go @@ -0,0 +1,78 @@ +// Copyright 2025 Siemens AG +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ld + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRdfToObject_NativeTypes(t *testing.T) { + testCases := []struct { + name string + literal Literal + expected map[string]any + }{ + { + name: "Boolean true", + literal: Literal{Value: "true", Datatype: XSDBoolean}, + expected: map[string]any{"@value": true}, + }, + { + name: "Boolean false", + literal: Literal{Value: "false", Datatype: XSDBoolean}, + expected: map[string]any{"@value": false}, + }, + { + name: "Boolean True", + literal: Literal{Value: "True", Datatype: XSDBoolean}, + expected: map[string]any{"@value": "True", "@type": XSDBoolean}, + }, + { + name: "Float", + literal: Literal{Value: "3.141", Datatype: XSDFloat}, + expected: map[string]any{"@value": float64(3.141)}, + }, + { + name: "Double", + literal: Literal{Value: "2.71828", Datatype: XSDDouble}, + expected: map[string]any{"@value": float64(2.71828)}, + }, + { + name: "Integer", + literal: Literal{Value: "42", Datatype: XSDInteger}, + expected: map[string]any{"@value": int64(42)}, + }, + { + name: "String without @type", + literal: Literal{Value: "hello world", Datatype: XSDString}, + expected: map[string]any{"@value": "hello world"}, + }, + { + name: "Decimal", + literal: Literal{Value: "3.141", Datatype: XSDDecimal}, + expected: map[string]any{"@value": "3.141", "@type": XSDDecimal}, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + converted, err := RdfToObject(tc.literal, true) + require.NoError(t, err) + assert.Equal(t, tc.expected, converted) + }) + } +}