0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【エラー解決】SyntaxError: Unexpected non-whitespace character after JSON

Posted at

前提

以下のアルゴリズム問題を解いていた時に発生したエラーです。

問題概要

整数 n が与えられたとき、以下の条件に基づいて出力を生成するラムダ関数 fizzbuzz を作成する

  • n が 3 の倍数であれば "Fizz" を生成
  • n が 5 の倍数であれば "Buzz" を生成
  • n が 15 の倍数であれば "FizzBuzz" を生成
  • 上記のどの条件にも当てはまらない場合は、数値 n 自体を生成

作成した関数を用いて、提供されたテストケースで関数の出力を表示すること
なお、出力は 1 から n までの各数値に対して上記のルールに基づき生成し、各値はハイフン - で区切って連結

テストケース

ケース 入力 出力
1 9 1-2-Fizz-4-Buzz-Fizz-7-8-Fizz
2 20 1-2-Fizz-4-Buzz-Fizz-7-8-Fizz-Buzz-11-Fizz-13-14-FizzBuzz-16-17-Fizz-19-Buzz

使用言語: JavaScript

エラー内容

テスト出力結果
undefined:1
1-2-Fizz-4-Buzz-Fizz-7-8-Fizz
 ^

SyntaxError: Unexpected non-whitespace character after JSON at position 1
    at JSON.parse (<anonymous>)
    at Object.<anonymous> (/Users/mavo/project/algorithm-solutions/HigherOrderFunc/problems/01_fizzbuzz/js/tests/fizzBuzzTest.js:16:30)
    at Module._compile (node:internal/modules/cjs/loader:1369:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1427:10)
    at Module.load (node:internal/modules/cjs/loader:1206:32)
    at Module._load (node:internal/modules/cjs/loader:1022:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12)
    at node:internal/main/run_main_module:28:49

Node.js v20.12.2

実装コード

fizzbuzz(ラムダ関数)
let fizzbuzz = n => {
    let resStr = "";

    for(let i = 1; i <= n; i++){
        if(i % 15 === 0) resStr += "-FizzBuzz";
        else if(i % 5 === 0) resStr += "-Buzz";
        else if(i % 3 === 0) resStr += "-Fizz";
        else resStr += `-${i}`;
    }

    return resStr.slice(1);
}

module.exports = fizzbuzz;
テストコード
const fizzbuzz = require('../src/fizzbuzz.js')

// テストケース
const tests = {
   "case1" : {
       "input" : 9,
       "output" : "1-2-Fizz-4-Buzz-Fizz-7-8-Fizz"
   },
   "case2" : {
       "input" : 20,
       "output" : "1-2-Fizz-4-Buzz-Fizz-7-8-Fizz-Buzz-11-Fizz-13-14-FizzBuzz-16-17-Fizz-19-Buzz"
   }
};

// 検証用ループ
for(let [key, value] of Object.entries(tests)){
    const res = fizzbuzz(value['input']);
    const finalResult = JSON.parse(res) === JSON.parse(value['output']) ? "True" : "False";
    console.log(`Test ${key}: ${finalResult}`);
}

エラー箇所の特定

エラー箇所
...
at JSON.parse (<anonymous>)
...

スタックトレースの最後にある、JSON.parse (<anonymous>)の部分がエラーの発生源とわかります。

エラーの意味

エラー内容
SyntaxError: Unexpected non-whitespace character after JSON at position 1

構文エラー: JSONとして有効な部分を読み取った後、2文字目(インデックス1)に予期しない非空白文字が見つかった

不正な JSON
JSON.parse が JSON の文法に適合しない文字列を受け取った場合、 SyntaxError が発生します。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse より引用

原因

通常のケース
→ JSONの構文ルールに違反している可能性

今回のケース
→ 文字列をJSONとして解釈させようとしたこと(JSON.parse() を使用していること

そもそも、この課題のゴールは文字列を出力することです。

そのために、期待される出力結果(文字列)と実際の処理結果(文字列)を比較して、正しいかどうかをテストしています。

このテストにおいては、期待される出力結果も実際の出力結果も文字列であるため、JSON.parse() を使う必要がなかったということです。

JSON.parse() の処理

JSON.parse()の内部動作(簡易版)
JSON.parse()に文字列が渡されると、パーサは文字列の先頭から1文字ずつ読み込みます。

  • 最初に読み込む文字が、JSONの有効な開始文字({, [, ", 0-9, t, f, n)であるかをチェックします
  • もし有効な文字であれば、パーサはそのデータ型(オブジェクト、配列、文字列、数値など)の解析を開始します
  • 有効な文字でなければ、即座にSyntaxErrorを投げます
    ※ Gemini の解説より引用

逐次、文字を読み取り、ルールに反していないかチェックしているようです。

今回のケースでは、「1」の後に「-」(ハイフン)があり、ハイフンは空白文字ではないため、JSONの構文ルールに違反していると判断し、エラーを投げたと判断できます。

つまり、有効なJSON要素(例: 数値1)の後に続く文字は、構文上許容される文字(数値の残りの部分、カンマ、閉じ括弧、あるいは空白)である必要があります。ハイフンはそれらのどれでもないため、予期せぬ文字として扱われたことを意味します。

有効なJSONの形式

主にオブジェクト配列の形式があります。

1. オブジェクト

オブジェクトは、キーと値のペアの集まりです。
キーは必ずダブルクォーテーション「""」で囲まれた文字列で、コロン「:」の後に値が続きます。
複数のペアはカンマ「,」で区切られます。

{
  "name": "mavo",
  "age": 25,
  "isStudent": false,
  "hobbies": ["reading", "walking"],
  "address": {
    "city": "Tokyo",
    "zipCode": "100-0001"
  }
}

2. 配列

配列の要素には、文字列、数値、オブジェクトなど、あらゆるJSONのデータ型を入れることができます。

[
  "apple",
  "banana",
  {
    "name": "orange",
    "color": "orange"
  },
  123,
  null
]

JSONで値として使えるデータ型

  1. 文字列: ダブルクォーテーション " で囲む(例: "Hello")
  2. 数値: 整数または小数(例: 123)
  3. ブール値: true または false
  4. null
  5. オブジェクト: {} で囲む
  6. 配列: [] で囲む

補足: JSON.stringify()

JSON.stringify() は、JavaScript の値(オブジェクトや配列など)を JSON 形式の文字列に変換する関数です。

例えば、FizzBuzz の結果を配列で保持している場合、そのままでは配列同士の比較が難しいですが、JSON.stringify() を使えば文字列として比較できます

const arr1 = [1, 2, "Fizz"];
const arr2 = [1, 2, "Fizz"];

console.log(JSON.stringify(arr1) === JSON.stringify(arr2)); 
// 出力: true

オブジェクトや配列をそのまま比較したいときには JSON.stringify() が有効です。

一方、今回の課題のように「最終出力が単なる文字列」である場合は、=== による比較で目的を達成できます。

まとめ

この記事で分かったこと

  • 目的に応じた手段を採用できているかチェックすること
  • JSONには有効な形式があること
  • JSON.parse()の内部処理
  • 構文チェックを逐次行なっていること

JSON.parse()は文字列に変換できるものだと勘違いしていたことが、今回のエラーを招いたことがわかりました。

最後までお読みいただき、ありがとうございました。

参考URL

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?