You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by li...@apache.org on 2026/10/15 10:10:46 UTC

(rocketmq-dashboard) branch rocketmq-studio updated: fix(rmqctl): preserve numeric literals in YAML output (#6439)

This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new eeaa90b91 fix(rmqctl): preserve numeric literals in YAML output (#6439)
eeaa90b91 is described below

commit eeaa90b91d66d0cf4038aacc124cc5facd136e67
Author: Loyal-Young <hj...@gmail.com>
AuthorDate: Thu Oct 15 18:10:40 2026 +0800

    fix(rmqctl): preserve numeric literals in YAML output (#6439)
---
 rmqctl/internal/output/output.go      | 23 +++++++++++++----------
 rmqctl/internal/output/output_test.go | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 46 insertions(+), 10 deletions(-)

diff --git a/rmqctl/internal/output/output.go b/rmqctl/internal/output/output.go
index 7f6fba71d..ea4afc44f 100644
--- a/rmqctl/internal/output/output.go
+++ b/rmqctl/internal/output/output.go
@@ -58,21 +58,24 @@ func YAML(w io.Writer, value any) error {
 	return encoder.Close()
 }
 
-// restoreNumbers turns json.Number values back into numbers. The studio client decodes with
-// UseNumber() so integer literals survive untouched, and json.Number is a string type: yaml.v3
-// would emit it quoted, so `size: 3` came out as `size: "3"` and the YAML no longer round-tripped
-// to the payload the server sent. The JSON path needs no such step because encoding/json marshals
-// json.Number as a bare literal.
+// restoreNumbers preserves JSON numeric literals as numeric YAML scalars. Converting through
+// int64/float64 would round large integers and precise decimals, or underflow small exponents.
+// Explicit tags also preserve numbers outside yaml.v3's implicit numeric resolution range.
 func restoreNumbers(value any) any {
 	switch typed := value.(type) {
 	case json.Number:
-		if i, err := typed.Int64(); err == nil {
-			return i
+		if typed == "" {
+			return ""
 		}
-		if f, err := typed.Float64(); err == nil {
-			return f
+		if _, err := json.Marshal(typed); err != nil {
+			return typed.String()
 		}
-		return typed.String()
+		literal := typed.String()
+		tag := "!!int"
+		if strings.ContainsAny(literal, ".eE") {
+			tag = "!!float"
+		}
+		return &yaml.Node{Kind: yaml.ScalarNode, Tag: tag, Value: literal}
 	case map[string]any:
 		restored := make(map[string]any, len(typed))
 		for key, entry := range typed {
diff --git a/rmqctl/internal/output/output_test.go b/rmqctl/internal/output/output_test.go
index 28e517647..b0689a92a 100644
--- a/rmqctl/internal/output/output_test.go
+++ b/rmqctl/internal/output/output_test.go
@@ -21,8 +21,41 @@ import (
 	"encoding/json"
 	"strings"
 	"testing"
+
+	"gopkg.in/yaml.v3"
 )
 
+func TestYAMLPreservesNumericLiteralsBeyondMachinePrecisionTest(t *testing.T) {
+	for _, literal := range []string{
+		"18446744073709551615", "-9223372036854775809",
+		"0.1234567890123456789", "1.234567890123456789e+20", "1e400", "1e-400",
+	} {
+		t.Run(literal, func(t *testing.T) {
+			decoder := json.NewDecoder(strings.NewReader(`{"items":[{"value":` + literal + `}]}`))
+			decoder.UseNumber()
+			var payload any
+			if err := decoder.Decode(&payload); err != nil {
+				t.Fatal(err)
+			}
+			buf := &bytes.Buffer{}
+			if err := YAML(buf, payload); err != nil {
+				t.Fatal(err)
+			}
+			var document yaml.Node
+			if err := yaml.Unmarshal(buf.Bytes(), &document); err != nil {
+				t.Fatal(err)
+			}
+			value := document.Content[0].Content[1].Content[0].Content[1]
+			if value.Value != literal || (value.Tag != "!!int" && value.Tag != "!!float") {
+				t.Fatalf("numeric literal changed: want %s, got %s (%s); YAML: %s", literal, value.Value, value.Tag, buf)
+			}
+			if original := payload.(map[string]any)["items"].([]any)[0].(map[string]any)["value"]; original != json.Number(literal) {
+				t.Fatalf("input was mutated: %v", original)
+			}
+		})
+	}
+}
+
 func TestYAMLRestoresJSONNumbersTest(t *testing.T) {
 	payload := map[string]any{
 		"items": []any{