0

I've found that function that correctly work for the JSON parse:

var objs = JObject.Load(new JsonTextReader(new StreamReader(JsonPath)))
                  .SelectTokens("*")
                  .Select(t => JsonConvert.DeserializeObject<Cars>(t.ToString()));

but it doesn't release the file after use it (so I can not use it for other function) ... I've tried to use the function Close, but seems not working over the object!

P.s. i've checked the previous open-one question, releated to this topic, but nobody talk about how to access to the object fileds, and seems the qeustion are different if we count that i have more than 1 object ... guess someone can explain better how it works ...

Zenek
  • 110
  • 1
  • 4
  • 12
  • 2
    Don't write such code. The file is kept open by the text reader. To close the text reader, you need to be able to access it. Putting all code in a single line is offers no benefits – Panagiotis Kanavos Jan 13 '21 at 17:01
  • What's the point of `JsonConvert.DeserializeObject(t.ToString()))` ??? What's the actual content of the JSON file? If you want to deserialize to a specific class use the deserializer directly. – Panagiotis Kanavos Jan 13 '21 at 17:05
  • Thanks for the reply! i really don't have an idea ... i need to admit ... i'm pretty new in c#, i just would like take the json and the read all the fields on it ... have you a better code to suggest to me ? (i found it in another topic on stack, i was studying json, i took it and it seems work...) – Zenek Jan 13 '21 at 17:10
  • Does this answer your question? [Loading a .json file into c# program](https://stackoverflow.com/questions/18538428/loading-a-json-file-into-c-sharp-program) – Liam Jan 13 '21 at 17:12
  • 1
    You can't just write code and expect it to work. JSON.NET has extensive documentation that shows how to parse or deserialize files. It's just a couple of lines in each case. Again, what does the file contain? – Panagiotis Kanavos Jan 13 '21 at 17:15
  • @Liam no, i've added an "PS that explain why" – Zenek Jan 13 '21 at 17:38
  • @Zenek, if you want to use JSON just for one time configuration of your app, then I can strongly recomment you to google for Microsoft.Extensions.Configuration. Now I use it in every app I code. There are 3 benefits: 1) Dependency Injection 2) You don't care about treating files 3) There is a simple way to validate data that have been read from config. – Ivan Khorin Jan 13 '21 at 18:01
  • thank you for the suggestion, but i need to manage data on the json, that's way i used that way! – Zenek Jan 13 '21 at 18:05
  • 1
    Time will come when you start drink whisky, love olivas and use DI :) – Ivan Khorin Jan 13 '21 at 18:07

3 Answers3

2

From the JSON.NET Site Read JSON from a File :

// read JSON directly from a file
using (StreamReader file = File.OpenText(@"c:\videogames.json"))
using (JsonTextReader reader = new JsonTextReader(file))
{
    JObject o2 = (JObject)JToken.ReadFrom(reader);
}

The question's code does something unnecessary though - it tries to serialize the already deserialized JObject back into JSON, then deserialize it again as a Car. Why not deserialize the original file just once?

The docs describe this too in Deserialize JSON from a file:

// deserialize JSON directly from a file
using (StreamReader file = File.OpenText(@"c:\movie.json"))
{
    JsonSerializer serializer = new JsonSerializer();
    Movie movie2 = (Movie)serializer.Deserialize(file, typeof(Movie));
}

JSON documents can contain only a single object. If the file contents look like this :

{
    "Movies":[{
                  "Title":"abc"
              }, {

                  "Title":"abc"
              }]
}

The root object should contain a Movies property that holds a Movie[] array :

class RootObjedt
{
    public Movie[] Movies{get;set;}
}

...

using (StreamReader file = File.OpenText(@"c:\movie.json"))
{
    JsonSerializer serializer = new JsonSerializer();
    var root = (Movie)serializer.Deserialize(file, typeof(RootObject));
    var movies=root.Movies;
}
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • (i'm looking at it before the implementation, i would like say thank you in advance for the reply! I'm studying it, and i will reply once finish) – Zenek Jan 13 '21 at 17:23
  • hmm .. i was looking at it ... whats happen if i have more than one object? In this particular case, i'm using the class "Cars" and the json file contain more the one object. Every object is a particular car! – Zenek Jan 13 '21 at 17:26
  • @Zenekn JSON doesn't allow more than one root object. Post a sample of your data. One can only guess what's in there right now. – Panagiotis Kanavos Jan 13 '21 at 17:27
  • you're right, thank you for your amazing support. that's my json: https://pastebin.com/nniFLYrm And that's my class: https://pastebin.com/H4TrS6eJ – Zenek Jan 13 '21 at 17:28
1

I recommend you to read your json data async because this method have overload that takes stream as an input, so you can read data in two strings of code like this:

ProjFolder\Program.cs

using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using StackOverflow.Models;

namespace StackOverflow
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using var openStream = File.OpenRead(@"D:\Code\test.json");

            var result = await JsonSerializer.DeserializeAsync<Root>(openStream);

            // You need to get car2 Costo
            var costo = result.Car2.Costo;
        }
    }    
}

ProjFolder\Models\Root.cs

using System.Text.Json.Serialization;

namespace StackOverflow.Models
{
    public class Root
    {
        [JsonPropertyName("car1")]
        public Car Car1 { get; set; }

        [JsonPropertyName("car2")]
        public Car Car2 { get; set; }
    }
}

ProjFolder\Models\Car.cs

namespace StackOverflow.Models
{
    public class Car
    {
        public string CasaAutomobilistica { get; set; }
        public string Modello { get; set; }
        public string AnnoImmatricolazione { get; set; }
        public string Targa { get; set; }
        public int KM { get; set; }
        public bool Finanziamento { get; set; }
        public int Porte { get; set; }
        public int Costo { get; set; }
    }
}
{
    "car1": 
    {
        "CasaAutomobilistica": "Fiat",
        "Modello": "500",
        "AnnoImmatricolazione": "2017",
        "Targa": "AZ978AG",
        "KM": 120000,
        "Finanziamento" : true,
        "Porte" : 5,
        "Costo" : 6000
    },
    "car2":
    {
        "CasaAutomobilistica": "BMW",
        "Modello": "Serie 1",
        "AnnoImmatricolazione": "2019",
        "Targa": "BC978AG",
        "KM": 150000,
        "Finanziamento" : false,
        "Porte" : 3,
        "Costo" : 12000
    }
}
Ivan Khorin
  • 827
  • 1
  • 5
  • 17
  • Oh ... that's nice ! There is some kind of statement that i can use for check once the loading is finished ? – Zenek Jan 13 '21 at 17:42
  • Actually what you need to do is to correctly code the model of the data you gonna deserialize. After that you work with it like with a simple object, or List of objects, or anything else in c#. If it is IEnumerable (List for example), you can use Linq. And if you are talkin on validation "is data deserialized", then DeserializeAsync will throw exception if something went wrong, you just need `try catch` expression to test it. – Ivan Khorin Jan 13 '21 at 17:50
  • i've try to use that code but it seems not working when you have more than 1 object, also i need to implement the task in another class so , it can't be called in the main :( – Zenek Jan 13 '21 at 18:08
  • It can work whith all valid jason data. If it is not, then your model is wrong. You can provide JSON you want to deserialize in your question and I'll make it work wuth my variant. – Ivan Khorin Jan 13 '21 at 18:10
  • yea yea, i mean: of course is my fault is somenthing doesn't work. I really appreciate the support ! That's my class: https://pastebin.com/H4TrS6eJand that's my json: https://pastebin.com/nniFLYrm – Zenek Jan 13 '21 at 18:12
  • I've updated the code. You can use https://json2csharp.com/ to automatically build your class model from json you get or use internal tools of Visual Studio (in some cases you need to modify the resulting code like I did). – Ivan Khorin Jan 13 '21 at 18:21
  • that's amazing ... i will instantly check it ! – Zenek Jan 13 '21 at 18:46
  • If i would like move the class "root" and "car" in to another file (for example Cars.cs) , how can i correctly do it without error in the main Program.cs. I'm asking to you cause i'm learning a lot from your code... amazing work, thank you for your time ! – Zenek Jan 13 '21 at 18:50
  • Updated. you need to declare classes as `public` so they are visible from another class. Then you need to import namespace to Program.cs `using StackOverflow.Models;` that's all. What is a namespase and public you can read in docks. If you think that my answer is what you need you can mark it like a solution. – Ivan Khorin Jan 13 '21 at 19:11
  • what an amazing explanation ... thank bro.. i will check all the materials you passed me! Have an nice night – Zenek Jan 13 '21 at 20:22
  • Amh .... i've checked it ... seems there is a [problem](https://prnt.sc/wmw65p) with the type/namespace in Root.cs! Just for let you know! – Zenek Jan 13 '21 at 20:59
  • Look at my code and at your code. Then try blame me again :) – Ivan Khorin Jan 13 '21 at 21:14
  • n unhandled exception of type 'System.Text.Json.JsonException' occurred in System.Private.CoreLib.dll The JSON value could not be converted to StackOverflow.Models.Root. – Zenek Jan 14 '21 at 01:00
  • Thanks Ivan! It works, just a question (i'm still studying it, thanks for the materials!): i've checked the class "Root.cs", and i i've found this statement `JsonPropertyName("carX")` ... so for every object that exist in to the JSON file, i need to declare a new object in this class? If i would like implement some kind of "add cars" function, i need to overwrite the json file + all time change the source code? – Zenek Jan 14 '21 at 10:57
  • @Zenek, the idea is very simple: there are some naming conventions in C#, for example Properties are named from the capital letter, so you can frequently see `public int Id {get;set;}` but properties in json can not lead this convention. when json prop is car1 and model property is Car1, attribute JsonPropertyName help you to map it correctly. So json property `id_main` can be mapped to class property `Id` by declaring it like `JsonPropertyName("id_main")`. – Ivan Khorin Jan 14 '21 at 15:25
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/227339/discussion-between-zenek-and-ivan-khorin). – Zenek Jan 14 '21 at 15:30
-1

First, read the file with surrounded by using block and do JSON operation Better place it in a separate method as below

private string readFile()
{
     string buffer = null;
     using (System.IO.FileStream stm = new 
         System.IO.FileStream("c:\\YourFile.txt",System.IO.FileMode.Open,  
         System.IO.FileAccess.Read, System.IO.FileShare.None))
         {
             using (System.IO.StreamReader rdr = new System.IO.StreamReader (stm))
             {
                            buffer=rdr.ReadToEnd();
             }
         }
      return buffer
}
private Object Deserialize(string content)
{
//do conversion here
}
Muthukumar R
  • 141
  • 1
  • 7
  • What does this have to do with JSON? `readFile` does what `File.ReadAllText` does – Panagiotis Kanavos Jan 13 '21 at 17:14
  • It is better to separate the multiple operations, Eg: let consider the scenario: If the file content is not JSON and get the error in deserializing. we might not know error due to file operation like (not exist, locked) or JSON operations – Muthukumar R Jan 13 '21 at 17:22
  • What multiple operations? If you want to read the entire file into memory, `File.ReadAllText` will do the job. With big files that's a *big* problem though, which is why StreamReader exists in the first place. Why *add an intermediate load step* when you can parse the data while reading it? – Panagiotis Kanavos Jan 13 '21 at 17:22
  • Operation 1: File reading, Operation 2: JSON deserialize – Muthukumar R Jan 13 '21 at 17:24
  • Loading everything in memory will result in a large object that will need to be garbage collected. Each time you load a file you'll allocate *another* large object. This may not look important in a console application that runs only once, but a web server will quickly eat up all available RAM – Panagiotis Kanavos Jan 13 '21 at 17:25
  • In any case, this answer doesn't answer the question – Panagiotis Kanavos Jan 13 '21 at 17:26
  • Is he going to load that big file? will it affect memory much? – Muthukumar R Jan 13 '21 at 17:29
  • Big as far as the garbage collector is concerned is 64KB. Again, the code you posted doesn't help at all. It does what `File.ReadAllText` does, there's no parsing, and the code simply. doesn't do what was asked - parse directly from a file. Which is something very common in all types of applications, both for performance *and* reliability reasons. If the file is locked you'll get the same exception one way or another. Loading everything in memory doesn't just waste RAM, it means your code has to wait for the file to be loaded before it starts parsing. – Panagiotis Kanavos Jan 13 '21 at 17:33
  • It's just such optimizations that made .NET Core run 10-100x times faster than .NET Old – Panagiotis Kanavos Jan 13 '21 at 17:34