<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dmitriy</title>
    <description>The latest articles on DEV Community by Dmitriy (@keelmatrix).</description>
    <link>https://dev.to/keelmatrix</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4115440%2F2b9adc51-6a68-42ef-abd2-f4f24abf2675.png</url>
      <title>DEV Community: Dmitriy</title>
      <link>https://dev.to/keelmatrix</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/keelmatrix"/>
    <language>en</language>
    <item>
      <title>JSON to CSV Can Quietly Lose Your Data — 7 Traps to Avoid</title>
      <dc:creator>Dmitriy</dc:creator>
      <pubDate>Tue, 08 Sep 2026 11:58:44 +0000</pubDate>
      <link>https://dev.to/keelmatrix/json-to-csv-can-quietly-lose-your-data-7-traps-to-avoid-2m15</link>
      <guid>https://dev.to/keelmatrix/json-to-csv-can-quietly-lose-your-data-7-traps-to-avoid-2m15</guid>
      <description>&lt;p&gt;Converting JSON to CSV looks like one of the easiest jobs in programming: take the objects, spread them into columns, done.&lt;/p&gt;

&lt;p&gt;The catch is that CSV is a flat table and JSON is a tree.&lt;/p&gt;

&lt;p&gt;Every step of that translation forces decisions about nesting, arrays, missing values, numbers, and schema changes. If those decisions are implicit, data can silently change meaning — or disappear entirely.&lt;/p&gt;

&lt;p&gt;Here are seven traps worth checking before you trust a JSON → CSV pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. “Just flatten it” can hide schema changes
&lt;/h2&gt;

&lt;p&gt;Consider a typical API response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"order"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"customer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ada"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"London"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"zip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SW1A 1AA"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"note"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rush"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A reasonable flattened CSV might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order.id,order.customer.name,order.customer.address.city,order.customer.address.zip,note
1001,Ada,London,SW1A 1AA,rush
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, so good.&lt;/p&gt;

&lt;p&gt;The problem is that “flatten” is not one algorithm.&lt;/p&gt;

&lt;p&gt;One converter may join paths with &lt;code&gt;.&lt;/code&gt;, another with &lt;code&gt;_&lt;/code&gt;, and another may flatten only one level before stringifying the rest.&lt;/p&gt;

&lt;p&gt;All three can produce valid CSV while giving you very different schemas.&lt;/p&gt;

&lt;p&gt;Before converting, you should be able to see the exact output columns — including nested paths — and understand how they were derived.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Arrays are where row counts change
&lt;/h2&gt;

&lt;p&gt;Now add an array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"customer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ada"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"B2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no single correct CSV representation for &lt;code&gt;items&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You could keep the whole array as JSON in one cell.&lt;/p&gt;

&lt;p&gt;You could join a primitive array into a string.&lt;/p&gt;

&lt;p&gt;Or you could explode the array into repeated rows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;orderId,customer,items.sku,items.qty
1,Ada,A1,2
1,Ada,B2,1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each choice changes the meaning of the output.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep&lt;/strong&gt; preserves one input record as one CSV row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Join&lt;/strong&gt; turns multiple values into one scalar cell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explode&lt;/strong&gt; increases the number of rows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why array behavior should ideally be configurable per path rather than controlled by one global “arrays” switch.&lt;/p&gt;

&lt;p&gt;Multiple arrays make this even more important. Exploding two sibling arrays can accidentally create a Cartesian product and multiply rows dramatically.&lt;/p&gt;

&lt;p&gt;A converter should never make that decision silently.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. NDJSON schema drift can make your header lie
&lt;/h2&gt;

&lt;p&gt;JSON Lines / NDJSON looks naturally streamable because each line is a separate object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"pageview"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"purchase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;19.95&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="nl"&gt;"trace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"t-9"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a converter derives the header only from the first record, it might produce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;id,payload.kind
1,pageview
2,purchase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two real fields disappeared:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;payload.amount&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trace&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A complete schema instead needs the union of fields across the input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;id,payload.kind,payload.amount,trace
1,pageview,,
2,purchase,19.95,t-9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For very large files, scanning the entire input before showing a preview may be expensive.&lt;/p&gt;

&lt;p&gt;That is fine — but the preview should say when it is based on a sample.&lt;/p&gt;

&lt;p&gt;A schema inferred from the first 1,000 rows of a 1 GB file is provisional. Calling it provisional is not a weakness; it is an important correctness signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;code&gt;null&lt;/code&gt; and missing are not the same thing
&lt;/h2&gt;

&lt;p&gt;These records are semantically different:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the first record, &lt;code&gt;a&lt;/code&gt; exists and is explicitly &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the second record, &lt;code&gt;a&lt;/code&gt; does not exist at all.&lt;/p&gt;

&lt;p&gt;A naive CSV conversion makes them look identical:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a,b
,1
,2
3,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CSV has no built-in distinction between “explicitly null” and “missing”.&lt;/p&gt;

&lt;p&gt;So you need a policy.&lt;/p&gt;

&lt;p&gt;Possible choices include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;collapse both to an empty cell;&lt;/li&gt;
&lt;li&gt;write a literal such as &lt;code&gt;null&lt;/code&gt; for explicit nulls;&lt;/li&gt;
&lt;li&gt;add presence columns when the distinction matters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any of those can be valid.&lt;/p&gt;

&lt;p&gt;The dangerous option is collapsing the distinction without realizing it happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Numbers can change before they ever reach CSV
&lt;/h2&gt;

&lt;p&gt;JSON number syntax is not limited to what JavaScript &lt;code&gt;Number&lt;/code&gt;, IEEE-754 doubles, or spreadsheet numeric cells can represent exactly.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;900719925474099312345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tiny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1e-400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"negativeZero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-0&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a converter parses those values into a floating-point type and later serializes them again:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the large integer can be rounded;&lt;/li&gt;
&lt;li&gt;an extremely small exponent can underflow;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-0&lt;/code&gt; can lose its original representation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If preserving numeric text matters, the converter needs to preserve the original JSON number lexeme instead of round-tripping through an imprecise numeric type.&lt;/p&gt;

&lt;p&gt;For identifiers, financial exports, scientific data, and audit pipelines, that difference matters.&lt;/p&gt;

&lt;p&gt;“Looks like the same number” is not always good enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Spreadsheet formula injection is easy to overlook
&lt;/h2&gt;

&lt;p&gt;CSV is often opened in Excel, Google Sheets, or another spreadsheet application.&lt;/p&gt;

&lt;p&gt;That means some cell values can be interpreted as formulas rather than plain data.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"=1+1"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"+cmd"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"-2"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"@channel"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"=HYPERLINK(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;http://example.com&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the class of problem commonly called CSV or formula injection.&lt;/p&gt;

&lt;p&gt;There are two broad strategies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Preserve the original value and warn or flag it.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Neutralize formula-like prefixes during export.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Neutralization changes the data, so it should be an explicit policy.&lt;/p&gt;

&lt;p&gt;And importantly, ordinary CSV quoting is not a security boundary here. A correctly quoted CSV cell can still be interpreted as a formula by spreadsheet software after import.&lt;/p&gt;

&lt;p&gt;The converter should at least identify formula-like values so the user knows what will happen downstream.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Large files turn memory usage into a correctness issue
&lt;/h2&gt;

&lt;p&gt;A 2 GB export is not just a bigger version of a 2 KB export.&lt;/p&gt;

&lt;p&gt;It is a different operating regime.&lt;/p&gt;

&lt;p&gt;Loading the entire document into a DOM, object graph, or DataFrame may exhaust memory long before conversion finishes.&lt;/p&gt;

&lt;p&gt;NDJSON can naturally be processed record by record.&lt;/p&gt;

&lt;p&gt;A giant top-level JSON array requires an incremental parser if you want bounded memory usage.&lt;/p&gt;

&lt;p&gt;The exact implementation varies, but the principle is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Input size should not force the converter to hold the entire dataset in memory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If a tool has a size limit, that is completely reasonable.&lt;/p&gt;

&lt;p&gt;What matters is that the limit is explicit instead of being discovered when the process consumes nearly all available RAM.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical checklist
&lt;/h2&gt;

&lt;p&gt;Before trusting a JSON → CSV pipeline, ask:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Can I see the exact output schema before converting?&lt;/strong&gt;&lt;br&gt;
Including flattened nested paths.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What happens to arrays?&lt;/strong&gt;&lt;br&gt;
Can I choose keep, join, or explode behavior per path where the array shape allows it?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How is the header discovered?&lt;/strong&gt;&lt;br&gt;
Is it based on the complete input, or is a sampled preview clearly marked as provisional?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What happens to &lt;code&gt;null&lt;/code&gt; versus missing fields?&lt;/strong&gt;&lt;br&gt;
Is that policy explicit?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Can values change during conversion?&lt;/strong&gt;&lt;br&gt;
Are number representations preserved when necessary, and are spreadsheet formula-like cells identified?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A converter that answers those questions explicitly is not doing anything magical.&lt;/p&gt;

&lt;p&gt;It is simply refusing to hide important decisions from you.&lt;/p&gt;

&lt;p&gt;That is the difference between “convert JSON to CSV” and “convert JSON to CSV without surprises.”&lt;/p&gt;




&lt;p&gt;Full disclosure: this is exactly the problem I built &lt;strong&gt;jsonnorm&lt;/strong&gt; to solve.&lt;/p&gt;

&lt;p&gt;It previews the schema, marks sampled previews as &lt;strong&gt;Provisional sample&lt;/strong&gt;, supports per-path keep/join/explode rules where applicable, distinguishes null from missing and lets you choose how each is written to CSV, preserves JSON number text, and flags formula-like cells before download.&lt;/p&gt;

&lt;p&gt;It is free, requires no sign-up, keeps no conversion history, and supports files up to 100 MiB.&lt;/p&gt;

&lt;p&gt;You can try two of the examples from this article directly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Array policies: &lt;a href="https://jsonnorm.com/examples?s=array-explode-rows&amp;amp;src=devto" rel="noopener noreferrer"&gt;https://jsonnorm.com/examples?s=array-explode-rows&amp;amp;src=devto&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Null vs. missing: &lt;a href="https://jsonnorm.com/examples?s=null-missing-both-empty&amp;amp;src=devto" rel="noopener noreferrer"&gt;https://jsonnorm.com/examples?s=null-missing-both-empty&amp;amp;src=devto&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is also an anonymous API — no account or API key required. POST JSON or NDJSON and get CSV back:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jsonnorm.com/api/?src=devto" rel="noopener noreferrer"&gt;https://jsonnorm.com/api/?src=devto&lt;/a&gt;&lt;/p&gt;

</description>
      <category>json</category>
      <category>csv</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
