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{