1

In the following

  const fetchData = async () => {
    try {
      const response = await fetch("faulty-endpoint");
      const data = await response.text()
      console.log('data', data)
    } catch (error) {
      console.error(error)
    } finally {
      setLoading(false)
    }
  }

How do I turn the response.text() data back to json? I can't do

const data = await response.json()

Because then I console.log nothing. I want it to be JSON format so I can save it into a useState array that I can map across.

dev_el
  • 2,357
  • 5
  • 24
  • 55
  • 1
    "Because then I console.log nothing" <--- What do you mean by this? You can use `console.log` with arbitrary objects, so you **can** use `await response.json()`. – Dai Feb 19 '22 at 05:26
  • 1
    Why not use `response.json()` in the first place? And it sounds like you don't actually want JSON, you want an object, which you can get with from a json string via [JSON.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse). – ray Feb 19 '22 at 05:27
  • @Dai if I console.log data after await response.json(), I don't console.log anything – dev_el Feb 19 '22 at 05:28
  • @rayhatfield I would like an array of objects to map across, using response.json() does not allow me to see the data. It seems i can only use response.text() to read the data from the fetch url – dev_el Feb 19 '22 at 05:29
  • `response.json()` will return the array, assuming the response is well-formed JSON, and there's no reason you can't `console.log` that object. If you're not seeing console output I suspect there's something else wrong. Either your request is failing or the response isn't what you think it is. – ray Feb 19 '22 at 05:37
  • @rayhatfield I think the response is not a well formed-JSON and is a string. After checking the Preview of how the data is formed, it seems to a string instead of the regular array of objects. – dev_el Feb 19 '22 at 05:46
  • @rayhatfield edited post to show the Preview. Could you confirm that it is not a well formed-JSON? – dev_el Feb 19 '22 at 05:48
  • It *does* look like valid JSON. Have you *removed* `response.text()` when you used `response.json()`? – FZs Feb 19 '22 at 06:16
  • yes, I replaced response.text() with response.json() and got nothing. Looking more closely, I see that there is a comma at the end of the array. Maybe that is messing up the response? – dev_el Feb 19 '22 at 06:20
  • Yes, valid JSON can't have trailing commas. Can you fix it where it's coming from? – FZs Feb 19 '22 at 06:39
  • You could chop the comma off the `response.text()` and then `JSON.parse()` it back into an object. – ray Feb 19 '22 at 06:40
  • @rayhatfield Sure you could, but I'd try to first fix the source (the backend) if it's fixable. – FZs Feb 19 '22 at 06:44
  • @rayhatfield The person who made the backend is telling me to fix it on the frontend, turn it into JSON, and use the data that way...is that even possible? – dev_el Feb 19 '22 at 06:44
  • @FZs OP says in a [comment below](https://stackoverflow.com/questions/71182406/how-to-convert-response-text-to-json-with-react?noredirect=1#comment125826192_71182594) that he doesn't control the API. – ray Feb 19 '22 at 06:45
  • 1
    @dev_el Yes, it's possible. See [youdateme's answer](https://stackoverflow.com/a/71182677/636077), or just do `const obj = JSON.parse(text.replace(/,$/, ''))`. – ray Feb 19 '22 at 06:46

5 Answers5

3

Because of that trailing comma, you can remove it first, then use JSON.parse:

let text = await response.text();

// if trailing comma present, remove last character by slicing it off
// -1 in slice is the same as text.length - 1
if (text.endsWith(",")) text = text.slice(0, -1);

const data = JSON.parse(text);

This isn't the best or cleanest solution however; ideally the API should return valid JSON...

kelsny
  • 23,009
  • 3
  • 19
  • 48
  • You could also do this with `JSON.parse(text.replace(/,$/, ''))`, eliminating the need for the conditional trimming. If the comma isn't there the replace doesn't do anything. – ray Feb 19 '22 at 06:44
  • Works excellently too! I just thought regex would be overkill/severely minor perf drain. – kelsny Feb 19 '22 at 06:45
  • With the trailing `$` it should be super efficient. – ray Feb 19 '22 at 06:47
  • [proof of concept](https://stackoverflow.com/a/71182995/636077) – ray Feb 19 '22 at 07:33
2

Here's a working proof-of-concept implementation of youdateme's answer.

I'm faking the request here to avoid CORS issues and such, but the response is copied from the endpoint in your question.

(Note: I had to re-escape the quotes around \"Hello World\" in the last record in the exported template string.)

It does what youdateme suggested and it works.

const fetchData = async () => {
  try {
    const response = await fakeRequest();
    const data = JSON.parse(response.replace(/,$/, ""));
    setData(data);
  } catch (error) {
    console.error(error);
  }
};

fetchData();

If this solves your problem you should accept youdateme's answer.

ray
  • 26,557
  • 5
  • 28
  • 27
0

JSON and a JavaScript object look similar, but they're different. JSON is only a string that's formatted like a JavaScript object. See: Working with JSON - MDN

To map across, you need it to be a JavaScript object, not JSON.

Use JSON.parse(yourResponse) to parse a JSON string into a JavaScript object (or array, etc.). See: JSON.parse from MDN

To convert a JavaScript object into a JSON string, you may use JSON.stringify(yourObject)

Dharman
  • 30,962
  • 25
  • 85
  • 135
  • 1
    Could you please apply your answer to the code above? I'm having difficulty understanding what you mean! – dev_el Feb 19 '22 at 05:37
0

Try this JSON.parse(responseData)

const fetchData = async () => {
try {
  const response = await fetch("faulty-endpoint");
  const data = await response.text()
  console.log('data', JSON.parse(data));
 } catch (error) {
  console.error(error)
 } finally {
  setLoading(false)
 }
}

Other solution:

fetch('targetedURL')
.then(response => response.json())
.then(data => console.log(data));
dev_el
  • 2,357
  • 5
  • 24
  • 55
Rashed Rahat
  • 2,357
  • 2
  • 18
  • 38
  • I tried `let test = JSON.parse(await response.text());` but console logging test gives me nothing! – dev_el Feb 19 '22 at 05:40
  • I get the following error SyntaxError: Unexpected token , in JSON at position 2756 at JSON.parse () at fetchData – dev_el Feb 19 '22 at 05:45
  • Please have a look at this https://stackoverflow.com/a/59192631/10427807 – Rashed Rahat Feb 19 '22 at 05:49
  • Have you tried this? fetch('http://example.com/movies.json') .then(response => response.json()) .then(data => console.log(data)); – Rashed Rahat Feb 19 '22 at 05:52
  • 1
    Yes I think why your solution is not working is because the data is not well-formed JSON and is a string, pls refer to my question where I added an image showing the Preview to not be an array of objects, but a string. – dev_el Feb 19 '22 at 05:53
  • Yes you got the point. Could you try the other solution of my answer? Should work. – Rashed Rahat Feb 19 '22 at 05:55
  • I get the same error: Uncaught (in promise) SyntaxError: Unexpected token , in JSON at position 2756 I'm pretty sure this is because the endpoint is not well-formed JSON. – dev_el Feb 19 '22 at 05:59
  • Yeah thats the issue – Rashed Rahat Feb 19 '22 at 06:38
0

The problem seems to the formatting of the response returned by the api. The api returns a trailing ',' at the end of the array which makes it an invalid JSON object. remove that ',' from the response and

const data = await response.json()

should work.

nthnua
  • 1
  • I can't remove the , from the response because I don't have control of that API endpoint. It seems this question is impossible to solve until the endpoint is fixed to give a proper JSON object instead of a string... – dev_el Feb 19 '22 at 06:08