Two properties of the same name with the same schema. Error while trying to get property value

I am trying to recursively run through a Json file and get a property named "fileName" and then add the value of that property to a ListView. However, the problem is that, as the name says, there are two instances of the same property in the same schema, which is causing the error in my opinion.

I want to ignore the "fileName" property containing "spigot.jar" and only retrieve the property that contains "spigot-1.7.10-R0.1-SNAPSHOT.jar".

Json sample I'm trying to parse (or use http://ci.md-5.net/job/Spigot/api/json?depth=1 as a reference):

"artifacts" : [
    {
      "displayPath" : "spigot-1.7.10-R0.1-SNAPSHOT.jar",
      "fileName" : "spigot-1.7.10-R0.1-SNAPSHOT.jar",
      "relativePath" : "Spigot-Server/target/spigot-1.7.10-R0.1-SNAPSHOT.jar"
    },
    {
      "displayPath" : "spigot.jar",
      "fileName" : "spigot.jar",
      "relativePath" : "Spigot-Server/target/spigot.jar"
    }
  ]

      


How I try to parse and add it to ListView in C #:

var url = "http://ci.md-5.net/job/Spigot/api/json?depth=1";
var content = (new WebClient()).DownloadString(url);

dynamic json = JsonConvert.DeserializeObject(content);

foreach (var builds in json.builds)
{
    string fileName = builds.artifacts.fileName;

    lvServers.Items.Add(fileName);
}

      


How can I successfully restore the "fileName" property?

+3


source to share


3 answers


Try the following. This will list the build number plus the first filename for each build:

var url = "http://ci.md-5.net/job/Spigot/api/json?depth=1";
var content = (new WebClient()).DownloadString(url);

JObject root = JObject.Parse(content);

var list = root["builds"].Select(b => 
           b["number"].ToString() + " - " + 
           b["artifacts"].Select(a => a["fileName"].ToString())
                         .FirstOrDefault());

foreach (var fileName in list)
{
    lvServers.Items.Add(fileName);
}

      

Demo : https://dotnetfiddle.net/54l8YX

Note that there are no artifacts in build # 1603 (I assume because the result was FAILURE for this build), so the filename is empty.


Explanation of what is happening in the above code



I am using the Json.Net LINQ-to-JSON API (JObjects, JTokens, JArrays, etc.) in combination with inline .NET from System.Linq namespace ( Select

, FirstOrDefault

) and a pair of lambda expressions to retrieve data from the JSON hierarchy.

This is how it breaks:

  • I first view the uploaded content in JObject

    with JObject.Parse(content)

    .

  • With help, JObject

    I can use the square bracket syntax (for example for Dictionary

    ) to get the value of an immediate child property inside that object. root["builds"]

    gives me an JArray

    of JObjects

    representing a list of builds.

  • The method Select

    lets me take IEnumerable

    something (in this case JArray

    of JObjects

    , representing assemblies), iterates over that list, and applies a function to each item in the list to convert it to a list of something else (in this case, a list of strings).

  • The function I apply to each assembly JObject

    is a lambda : expression b => b["number"] + " - " + b["artifacts"] ...

    . In this expression, I say "from assembly b

    , get a property number

    as a string, concatenate it with a delimiter -

    and sub-expression that gets the first filename from the list of artifacts to build.

  • In the subexpression, I get the value of the artifacts

    assembly property (which is another JArray

    of JObjects

    ), then use Select

    to convert it to a list of filenames with a lambda expression a => a[fileName].ToString()

    . But, since I only want the first filename, I use FirstOrDefault()

    to filter the list to one element (or null if there are no elements).

Hope this makes sense. If you are not familiar with LINQ or lambda expressions, then the code will seem a little cryptic. Below is an alternate version that does not use these constructs, but does the same. This might be a little easier to understand.

var url = "http://ci.md-5.net/job/Spigot/api/json?depth=1";
var content = (new WebClient()).DownloadString(url);

JObject root = JObject.Parse(content);

foreach (JObject build in root["builds"])
{
    string buildName = build["number"].ToString() + " - ";

    foreach (JObject artifact in build["artifacts"])
    {
        JToken fileName = artifact["fileName"];
        if (fileName != null)
        {
            buildName += fileName.ToString();   
        }
        break;
    }

    lvServers.Items.Add(buildName);
}

      

Demo : https://dotnetfiddle.net/vwebrY

+3


source


It looks like artifacts are an array, so you either have to iterate over them or access them with an indexer:

foreach (var artifact in builds.artifacts)
{
    var fileName = artifact.fileName;
}

      



or

var fileName = builds.artifacts[0].fileName;

      

+4


source


Strong typing converting JSON objects to C # will lead to compile time errors. Since you are using a dynamic keyword, you will not get compiler errors until you try to run the application.

Create these classes: Classes

Then change your current code like this:

var url = "http://ci.md-5.net/job/Spigot/api/json?depth=1";
var content = (new WebClient()).DownloadString(url);

var responseObj = JsonConvert.DeserializeObject<RootObject>(content);

      

Since they are now heavily typed, you can get compiler errors to show you that you are doing it wrong. Try to stay away from the dynamic object.

Now it will be a mistake because you are not using it correctly:

foreach (var builds in json.builds)
{
    string fileName = builds.artifacts.fileName;

    lvServers.Items.Add(fileName);
}

      

Now your code will throw an error like this to tell you that it is wrong:

'System.Collections.Generic.IEnumerable<UserQuery.Artifact>' does not contain a definition for 'fileName' and no extension method 'fileName' accepting a first argument of type 'System.Collections.Generic.IEnumerable<UserQuery.Artifact>' could be found (press F4 to add a using directive or assembly reference)

      

If you need to create a future class, use the JSON2CSHARP tool . Just be aware that you will need to dig up duplicates, although this is terrible.

0


source







All Articles