Skip to content

[pdata/common] Marshal Value to JSON bytes using the OTLP/JSON format #12826

@zhengkezhou1

Description

@zhengkezhou1

Component(s)

pdata

Describe the issue you're reporting

Background

We aim to store the complex types (map, slice) of value within attributes as JSON safely. Currently, serializing them to JSON strings using value.AsString() leads to the risk of losing type information during deserialization. Specifically:

  1. All numerical values become JSON numbers, making it impossible to distinguish whether a value was originally an int64 or a float64.
  2. Parsing nested map and slice structures is particularly problematic due to their dynamic and unpredictable nesting levels.

Solution

This issue can be resolved with a minor modification. Instead of directly serializing and deserializing the high-level Value type, we will operate on the underlying otlpcommon.AnyValue type it holds. AnyValue is a Go struct generated from Protobuf definitions, which allows for a more precise representation of different data types.

Here is the code implementing this functionality:

import oteljson "go.opentelemetry.io/collector/pdata/internal/json"
import "go.opentelemetry.io/collector/pdata/internal"
import "go.opentelemetry.io/proto/otlp/common/v1"

// UnmarshalValue deserializes a JSON byte stream into a Value type.
// It uses oteljson.ReadValue to read the JSON data into an AnyValue,
// and then creates a new Value instance using the AnyValue.
func UnmarshalValue(buf []byte) Value {
	iter := jsoniter.ConfigFastest.BorrowIterator(buf)
	defer jsoniter.ConfigFastest.ReturnIterator(iter)

	var anyValue v1.AnyValue // Using the correct Protobuf-generated AnyValue type
	state := internal.StateMutable
	oteljson.ReadValue(iter, &anyValue)
	return newValue(&anyValue, &state)
}

// MarshalValue serializes a Value type into a JSON byte stream.
// It retrieves the underlying AnyValue using value.getOrig()
// and then uses oteljson.Marshal to serialize the AnyValue into JSON.
func MarshalValue(value Value) ([]byte, error) {
	var buf bytes.Buffer
	if err := oteljson.Marshal(&buf, value.getOrig()); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions