Skip to content

feat: add PrettyFormatInlineArrays option to keep arrays on single line #2972#3930

Open
coffeeandwork wants to merge 3 commits intoalibaba:mainfrom
coffeeandwork:fix_2972
Open

feat: add PrettyFormatInlineArrays option to keep arrays on single line #2972#3930
coffeeandwork wants to merge 3 commits intoalibaba:mainfrom
coffeeandwork:fix_2972

Conversation

@coffeeandwork
Copy link

@coffeeandwork coffeeandwork commented Jan 3, 2026

What this PR does / why we need it?

Fixes #2972

Added a new Context configuration option that, when used with PrettyFormat, keeps array elements on a single line instead of expanding each element to its own line. This matches the original fastjson behavior.

Summary of your change

  • Added Context.setPrettyFormatInlineArrays(boolean) for per-call configuration
  • Added JSONFactory.setDefaultWriterPrettyFormatInlineArrays(boolean) for global default
  • Added levelArray bitmask to track array vs object context at each level
  • Modified startArray/endArray/writeComma in JSONWriterUTF8 and JSONWriterUTF16
  • Added comprehensive tests for edge cases

Usage

JSONWriter.Context context = new JSONWriter.Context(JSONWriter.Feature.PrettyFormat);
context.setPrettyFormatInlineArrays(true);
String json = JSON.toJSONString(obj, context);

// Or set global default
JSONFactory.setDefaultWriterPrettyFormatInlineArrays(true);

Example output

  // PrettyFormat only:
  {
  	"id": 123,
  	"values": [
  		1,
  		2,
  		3
  	]
  }

  // PrettyFormat + setPrettyFormatInlineArrays(true):
  {
  	"id": 123,
  	"values": [1,2,3]
  }

Uses Context configuration instead of a Feature bit to conserve the limited bits in the 64-bit Feature bitmask.

Please indicate you've done the following:

  • Made sure tests are passing and test coverage is added if needed.
  • Made sure commit message follow the rule of Conventional Commits specification.
  • Considered the docs impact and opened a new docs issue or PR with docs changes if needed.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coffeeandwork
Copy link
Author

I have signed the CLA. Not sure why its still showing as not signed yet. @wenshao help, what am i doing wrong?

@jujn
Copy link
Collaborator

jujn commented Jan 16, 2026

Hi @coffeeandwork ,
Thank you for submitting this PR! I see that the code implementation is very complete, and the test cases are also well-covered.

However, we are currently facing a tricky architectural limitation. In order to pursue ultimate performance, Fastjson2 uses a long (64-bit) to store all JSONWriter.Feature values. Currently, the available bits for this long are already very tight (only a dozen left).

Considering future scalability, we need to reserve these remaining bits for features that are practical, related to functional correctness, security, or core performance. Since PrettyFormatInlineArrays is primarily a formatting beautification feature, which is more of an "addition to excellence", we are hesitant to consume a permanent Feature bit.

@coffeeandwork coffeeandwork changed the title feat: add PrettyFormatInlineArrays feature to keep arrays on single line #2972 feat: add PrettyFormatInlineArrays option to keep arrays on single line #2972 Jan 17, 2026
@coffeeandwork
Copy link
Author

Hi @coffeeandwork , Thank you for submitting this PR! Bringing back this behavior from Fastjson 1.x is very meaningful. I see that the code implementation is very complete, and the test cases are also well-covered.

However, we are currently facing a tricky architectural limitation. In order to pursue ultimate performance, Fastjson2 uses a long (64-bit) to store all JSONWriter.Feature values. Currently, the available bits for this long are already very tight (only a dozen left).

Considering future scalability, we need to reserve these remaining bits for features that are practical, related to functional correctness, security, or core performance. Since PrettyFormatInlineArrays is primarily a formatting beautification feature, which is more of an "addition to excellence", we are hesitant to consume a permanent Feature bit.

Is it possible to implement this functionality without adding new enumeration values to the JSONWriter.Feature? For instance, through some kind of Context configuration or a separate formatting strategy class?

Hi @jujn,

Thank you for the detailed feedback! Your concern about preserving the limited Feature bits makes complete sense.

I've updated the PR to address this. The latest commit removes PrettyFormatInlineArrays from the Feature enum and instead uses Context configuration, following the same pattern as other non-Feature options like setDateFormat(), setZoneId(), and setMaxLevel().

The new API:

  // Per-call configuration
  JSONWriter.Context context = new JSONWriter.Context(JSONWriter.Feature.PrettyFormat);
  context.setPrettyFormatInlineArrays(true);
  String json = JSON.toJSONString(obj, context);

  // Or set global default
  JSONFactory.setDefaultWriterPrettyFormatInlineArrays(true);

This approach preserves all existing Feature bits while still providing the functionality. Let me know if you'd like any further changes!

@jujn
Copy link
Collaborator

jujn commented Jan 17, 2026

Thank you for your quick modification.
Could you please explain in detail the specific scenarios where this format must be used?
Fastjson 2.x did intentionally remove many non core features from 1.x in its design to maintain simplicity. If it is only for the sake of compatibility with the behavior of 1. x, it seems unnecessary?

@coffeeandwork
Copy link
Author

coffeeandwork commented Jan 17, 2026

Thank you for your quick modification. Could you please explain in detail the specific scenarios where this format must be used? Fastjson 2.x did intentionally remove many non core features from 1.x in its design to maintain simplicity. If it is only for the sake of compatibility with the behavior of 1. x, it seems unnecessary?

Hi @jujn,

Here are specific scenarios where inline array formatting provides significant value:

1. Primitive/Simple Value Arrays

Arrays of IDs, numbers, or simple values become excessively verbose when expanded:

Without inline arrays (15 lines):

{
    "type": 1001,
    "job_id": [
        0,
        45732
    ],
    "user_ids": [
        1297059520,
        4117498193,
        8291829301
    ],
    "is_active": true
}

With inline arrays (6 lines):

  {
      "type": 1001,
      "job_id": [0,45732],
      "user_ids": [1297059520,4117498193,8291829301],
      "is_active": true
  }
  1. GeoJSON and Coordinate Data

GeoJSON is a widely-used standard for geographic data. Coordinates are conventionally written inline - expanded format makes it nearly impossible to visualize the shape:

Without inline arrays (25 lines for a simple polygon):

  {
      "type": "Polygon",
      "coordinates": [
          [
              [
                  100.0,
                  0.0
              ],
              [
                  101.0,
                  0.0
              ],
              [
                  101.0,
                  1.0
              ],
              [
                  100.0,
                  0.0
              ]
          ]
      ]
  }

With inline arrays (8 lines):

  {
      "type": "Polygon",
      "coordinates": [
          [[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,0.0]]
      ]
  }
  1. Matrix and Tabular Data

Scientific computing, game development, and data processing often use matrices. Inline arrays preserve the visual structure:

Without inline arrays (38 lines for a 3x3 matrix):

  {
      "name": "rotation_matrix",
      "data": [
          [
              1,
              0,
              0
          ],
          [
              0,
              0.866,
              -0.5
          ],
          [
              0,
              0.5,
              0.866
          ]
      ]
  }

With inline arrays (7 lines):

  {
      "name": "rotation_matrix",
      "data": [
          [1,0,0],
          [0,0.866,-0.5],
          [0,0.5,0.866]
      ]
  }
  1. Configuration Files in Version Control

When config files are stored in git, inline arrays produce cleaner diffs:

Expanded format diff (adding one port):

    "allowed_ports": [
        8080,
        8443,
  +     9000
    ]

Shows 4 lines changed, harder to review in large configs.

Inline format diff:

  • "allowed_ports": [8080,8443]
  • "allowed_ports": [8080,8443,9000]
    Single line change, immediately clear what changed.
  1. Logging and Debugging Output

When logging JSON for debugging, inline arrays make logs scannable:

Expanded (requires scrolling, loses context):

  2024-01-15 10:23:45 DEBUG Response:
  {
      "status": "success",
      "error_codes": [
          0,
          0,
          0
      ],
      "processed_ids": [
          101,
          102,
          103,
          104,
          105
      ]
  }
  2024-01-15 10:23:46 DEBUG Next request...

Inline (compact, preserves log context):

  2024-01-15 10:23:45 DEBUG Response:
  {
      "status": "success",
      "error_codes": [0,0,0],
      "processed_ids": [101,102,103,104,105]
  }
  2024-01-15 10:23:46 DEBUG Next request...
  1. Test Fixtures and Documentation

Test data and API documentation examples are more maintainable:

Without inline arrays (test file becomes hundreds of lines):

  {
      "test_input": {
          "rgb_color": [
              255,
              128,
              0
          ],
          "position": [
              10.5,
              20.3,
              5.0
          ],
          "tags": [
              "active",
              "premium",
              "verified"
          ]
      }
  }

With inline arrays (concise, easy to maintain):

  {
      "test_input": {
          "rgb_color": [255,128,0],
          "position": [10.5,20.3,5.0],
          "tags": ["active","premium","verified"]
      }
  }

The common thread across all these scenarios is that arrays often represent atomic data units (a coordinate pair, a color, a row of values) that are more meaningful when kept together, while objects represent structured entities that benefit from expanded formatting.

I understand the desire to keep fastjson2 lean. If you feel these use cases aren't common enough to warrant inclusion, I respect that decision.

@wenshao
Copy link
Member

wenshao commented Jan 18, 2026

This will cause problems if the array length is too large.

@coffeeandwork
Copy link
Author

This will cause problems if the array length is too large.

Yeah that's a fair point. Though in practice, I think it's less of an issue than it might seem - the use cases where you'd want inline arrays (coordinates, RGB values, matrix rows, small ID lists) tend to be small arrays anyway. Anyone working with huge arrays probably wouldn't enable this option.

Also worth noting that without pretty printing, arrays already end up on a single line regardless of size - so this isn't introducing new behavior, just giving users a middle ground between fully expanded and fully compact.

That said, if you think it's important, I could add a max length threshold where arrays beyond N elements fall back to the expanded format. Let me know if you'd want that for the initial implementation or if it's fine as-is.

@wenshao
Copy link
Member

wenshao commented Jan 20, 2026

For primitive type arrays with a length less than 5, it might be better to put them on a single line by default.

@coffeeandwork
Copy link
Author

For primitive type arrays with a length less than 5, it might be better to put them on a single line by default.

I can implement this if this is truly what we want. Are you sure we want to change the default behavior in this way? Wouldn't it impact projects that rely on it? Please advise.

Fixes alibaba#2972

Added new JSONWriter.Feature.PrettyFormatInlineArrays that, when used with
PrettyFormat, keeps array elements on a single line instead of expanding
each element to its own line. This matches the original fastjson behavior.

Changes:
- Added PrettyFormatInlineArrays feature flag (bit 46)
- Added levelArray bitmask to track array vs object context at each level
- Modified startArray/endArray/writeComma in JSONWriterUTF8 and JSONWriterUTF16
- Added comprehensive tests for edge cases
…ead of Feature bit

Address review feedback about limited bits in 64-bit Feature bitmask.
Move inline arrays configuration from JSONWriter.Feature enum to
JSONWriter.Context, following the pattern used by other non-Feature
configuration like dateFormat, zoneId, and maxLevel.

API changes:
- Remove PrettyFormatInlineArrays from JSONWriter.Feature enum
- Add Context.setPrettyFormatInlineArrays(boolean) for per-call config
- Add JSONFactory.setDefaultWriterPrettyFormatInlineArrays(boolean) for global default

The implementation uses a levelArray bitmask to track array vs object
context at each nesting level, enabling proper indentation behavior
where objects remain pretty-printed while arrays stay inline.
Short primitive arrays (int[], byte[], short[], long[], float[], double[],
boolean[]) with fewer than 5 elements now render on a single line when
pretty-printing, improving readability for common cases like coordinates
[1, 2, 3]. Arrays with 5+ elements use proper multi-line formatting.
@coffeeandwork
Copy link
Author

Short primitive arrays (< 5 elements) now render inline in pretty-print mode by default. Arrays with 5+ elements use multi-line formatting. The existing PrettyFormatInlineArrays option still forces all arrays inline regardless of length.

Benchmarks (JDK 22, Apple M4 Max, 1 fork, 3 warmup / 5 measurement iterations):

Benchmark Before After
EishayWriteString (UTF16) 5,946,941 ± 55,390 ops/s 5,887,561 ± 34,412 ops/s
EishayWriteUTF8Bytes (UTF8) 6,739,323 ± 40,674 ops/s 6,700,563 ± 29,075 ops/s

Differences are within error margins. No measurable impact on the non-pretty hot path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[QUESTION]如何让fastjson2的PrettyFormat输出达到fastjson之前一样的效果?

5 participants