JSON serializer writes excessive ']'

Viewed 138

I have an API controller, which accepts HTTP requests and return some result. All work properly, but when it gets PUT request, serializer writes data with the excessive ']', so when I want to get data from JSON file after this, the program returns errors.

Controller API code:

using Gazda.Models;
using Gazda.Services;
using Microsoft.AspNetCore.Mvc;
using System;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace Gazda.Controllers
{
    [Route("/api/Comment")]
    [ApiController]
    public class CommentController : Controller
    {
        public CommentController(JsonFileCommentService jsonFileCommentService)
        {
            JsonFileCommentService = jsonFileCommentService;
        }
        public JsonFileCommentService JsonFileCommentService { get;}

        [HttpPost]
        public IActionResult Post([FromForm]Comment comment)
        {

            comment.Id = Guid.NewGuid().ToString();

            JsonFileCommentService.AddComment(comment);

            return Ok(comment);
        }

        [HttpGet]
        public JsonResult Get()
        {
            var comments = JsonFileCommentService.GetComments();
            return Json(comments);
        }

        [HttpPut]
        public IActionResult Put([FromBody] string id)
        {
            JsonFileCommentService.ChangeLike(id);
            return Ok("Successfull PUT");
        }

    }
}

JSON file before PUT-request:

[
  {
    "Id": "9d2a1e79-cd44-43a0-88cd-0d8868cd723e",
    "Name": "Yurii",
    "Text": "\u043D\u0430\u0434\u0437\u0432\u0438\u0447\u0430\u0439\u043D\u043E \u0441\u043C\u0430\u0447\u043D\u043E",
    "isLiked": false
  },
  {
    "Id": "e8577368-9634-40cc-ba30-7e3efd8f7ec3",
    "Name": "Arch",
    "Text": "super taste",
    "isLiked": true
  }
]

JSON file after PUT-request:

[
  {
    "Id": "9d2a1e79-cd44-43a0-88cd-0d8868cd723e",
    "Name": "Yurii",
    "Text": "\u043D\u0430\u0434\u0437\u0432\u0438\u0447\u0430\u0439\u043D\u043E \u0441\u043C\u0430\u0447\u043D\u043E",
    "isLiked": false
  },
  {
    "Id": "e8577368-9634-40cc-ba30-7e3efd8f7ec3",
    "Name": "Arch",
    "Text": "super taste",
    "isLiked": false
  }
]]

Service code:

using Gazda.Models;
using Microsoft.AspNetCore.Hosting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;

namespace Gazda.Services
{
    public class JsonFileCommentService
    {
        public JsonFileCommentService(IWebHostEnvironment webHostEnvironment)
        {
            WebHostEnvironment = webHostEnvironment;
        }
        
        IWebHostEnvironment WebHostEnvironment { get; set; }

        private string JsonFileName
        {
            get { return Path.Combine(WebHostEnvironment.WebRootPath, "data", "comments.json"); }
        }

        private void SerializeComments(List<Comment> comments)
        {
            using (var outputStream = File.OpenWrite(JsonFileName))
            {
                JsonSerializer.Serialize<IEnumerable<Comment>>(
                    new Utf8JsonWriter(outputStream, new JsonWriterOptions
                    {
                        SkipValidation = true,
                        Indented = true
                    }), comments);
            }
        }
        
        public List<Comment> GetComments()
        {
            try
            {
                using (var jsonFileReader = File.OpenText(JsonFileName))
                {
                    return JsonSerializer.Deserialize<List<Comment>>(jsonFileReader.ReadToEnd(),
                        new JsonSerializerOptions
                        {
                            PropertyNameCaseInsensitive = true
                        });
                }
            }
            catch
            {
                return new List<Comment>();
            }
        }

        public void AddComment(Comment comment)
        {
            if(comment==null)
            {
                throw new ArgumentNullException();
            }

            var comments = GetComments();

            comments.Add(comment);

            SerializeComments(comments);
        }

        public void ChangeLike(string id)
        {
            var comments = GetComments();

            var comment = comments.First(x=> x.Id == id);

            comment.isLiked = !comment.isLiked;

            SerializeComments(comments);
        }
    }
}

The most interesting thing, that service serialize data clearly, when controller gets POST-request. So, I don't understand, why it's happening. Thank you in advance!!!

P.S. For additional information here is my React code, where I make put request:

import React, { Component } from 'react'
import axios from 'axios';

export default class Comment extends Component {
    constructor(props){
        super(props);

        this.click = this.click.bind(this);
    }
    
    click(event){
        event.target.classList.toggle("likes");
        console.log(this.props.comment.id)
        axios({
            method: 'PUT',
            url: '/api/Comment', 
            data: JSON.stringify(this.props.comment.id), 
            headers:{'Content-Type': 'application/json; charset=utf-8'}
        })    
             .then(resp=>console.log(resp.data))
             .catch((err) => { throw err });
    }

    componentDidMount(){
        console.log("I'm inside component")
    }

    render() {
        return (
            <div>
                <div className="comment-item">
                    <div className="comment-name">{this.props.comment.name}: 
            </div>
                    <div className="comment-text">{this.props.comment.text}</div>
                    <i className="fas fa-heart" onClick={this.click}></i>
                    
                </div>
            </div>
        )
    }
}

1 Answers

To ensure that the full content of the output file gets overridden, you should open the file using:

var outputStream = File.Open(JsonFileName, FileMode.Create)  

When opened via File.OpenWrite it does an update of its content; but only the amount of characters present in the updated data change.
When the original data has more characters, those extra characters still remain in this file.


From the File.OpenWrite documentation:

For an existing file, it does not append the new text to the existing text.
Instead, it overwrites the existing characters with the new characters.
If you overwrite a longer string (such as "This is a test of the OpenWrite method")
with a shorter string (such as "Second run"), the file will contain a mix of the strings ("Second runtest of the OpenWrite method").

Related