0

My application is receive a json string. I want to be able to display this string in a nice formatted way. Truly I do not even know what question to ask and that is the source of my problem.

Here is an example of the String that I am receiving:

[{"sentence" : "Goldman Dukes is testing to see whether our request functionality works for the upcoming sprint.","sentenceNbr" : "1","tokens" : ["Goldman", "Dukes", "is", "testing", "to", "see", "whether", "our", "request", "functionality", "works", "for", "the", "upcoming", "sprint", "."],"pos" : ["NNP", "NNP", "VBZ", "VBG", "TO", "VB", "IN", "PRP$", "NN", "NN", "VBZ", "IN", "DT", "VBG", "NN", "."],"ner" : ["PERSON", "PERSON", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O"],"lemmas" : ["Goldman", "Dukes", "be", "test", "to", "see", "whether", "we", "request", "functionality", "work", "for", "the", "upcome", "sprint", "."]},{"sentence" : "Nick Wills is a great guy.","sentenceNbr" : "2","tokens" : ["Nick", "Wills", "is", "a", "great", "guy", "."],"pos" : ["NNP", "NNP", "VBZ", "DT", "JJ", "NN", "."],"ner" : ["PERSON", "PERSON", "O", "O", "O", "O", "O"],"lemmas" : ["Nick", "Wills", "be", "a", "great", "guy", "."]},{"sentence" : "He lives in Northern Virginia.","sentenceNbr" : "3","tokens" : ["He", "lives", "in", "Northern", "Virginia", "."],"pos" : ["PRP", "VBZ", "IN", "NNP", "NNP", "."],"ner" : ["O", "O", "O", "LOCATION", "STATE_OR_PROVINCE", "O"],"lemmas" : ["he", "live", "in", "Northern", "Virginia", "."]}]

I receive the strings exactly as above, with no whitespace or other formatting aids. Here's a slightly easier-to-read version:

[
  {
    "sentence" : "Goldman Dukes is testing to see whether our request functionality works for the upcoming sprint.",
    "sentenceNbr" : "1",
    "tokens" : ["Goldman", "Dukes", "is", "testing", "to", "see", "whether", "our", "request", "functionality", "works", "for", "the", "upcoming", "sprint", "."],
    "pos" : ["NNP", "NNP", "VBZ", "VBG", "TO", "VB", "IN", "PRP$", "NN", "NN", "VBZ", "IN", "DT", "VBG", "NN", "."],
    "ner" : ["PERSON", "PERSON", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O"],
    "lemmas" : ["Goldman", "Dukes", "be", "test", "to", "see", "whether", "we", "request", "functionality", "work", "for", "the", "upcome", "sprint", "."]
  },
  {
    "sentence" : "Nick Wills is a great guy.",
    "sentenceNbr" : "2",
    "tokens" : ["Nick", "Wills", "is", "a", "great", "guy", "."],
    "pos" : ["NNP", "NNP", "VBZ", "DT", "JJ", "NN", "."],
    "ner" : ["PERSON", "PERSON", "O", "O", "O", "O", "O"],
    "lemmas" : ["Nick", "Wills", "be", "a", "great", "guy", "."]
  },
  {
    "sentence" : "He lives in Northern Virginia.",
    "sentenceNbr" : "3",
    "tokens" : ["He", "lives", "in", "Northern", "Virginia", "."],
    "pos" : ["PRP", "VBZ", "IN", "NNP", "NNP", "."],
    "ner" : ["O", "O", "O", "LOCATION", "STATE_OR_PROVINCE", "O"],
    "lemmas" : ["he", "live", "in", "Northern", "Virginia", "."]
  }
]

My end goal is to display this data in a gridview type of format, but for now I would be satisfied with just figuring out how to display this in a "pretty" way, as above.

I am very familiar with using C# but have no experience with JSON. Any help would be appreciated

enter image description here

  • Maybe this: https://www.nuget.org/packages/FracturedJson But generally speaking, questions asking for tool recommendations are considered off-topic here. – Joel Coehoorn Nov 01 '21 at 21:43
  • Can you mock up what you expect this GV to look like, maybe take a screenshot of Excel? – Caius Jard Nov 01 '21 at 21:44
  • Since you're familiar with C#, this should be a breeze. Imagine the JSON is XML, and you're going to deseralize it into objects, like you would XML. Or imagine it's data you're loading from a database. Define your classes with properties like the keys in the JSON ("sentence", "tokens", etc), and then use either System.Text.Json (https://learn.microsoft.com/en-us/dotnet/api/system.text.json?view=net-5.0) or Newtonsoft.Json (https://www.newtonsoft.com/json) to turn the JSON string into objects. Then display those objects like you would any other c# object. – gnud Nov 01 '21 at 21:45
  • @CaiusJard I uploaded sample of what this could like – Vinson Sack Nov 01 '21 at 21:50
  • @gnud would the data type for the properties tokens, pos, ner, lemmas all be string [] ? – Vinson Sack Nov 01 '21 at 21:54
  • *for now I would be satisfied with just figuring out how to display this in a "pretty" way, as above* - deserialize it then reserialize it with `Formatting.Indented` - https://stackoverflow.com/questions/2661063/how-do-i-get-formatted-json-in-net-using-c – Caius Jard Nov 01 '21 at 22:13
  • To turn JSON into C# paste the json into http://quicktype.io then read the comments in the generated c# - it's a one liner – Caius Jard Nov 01 '21 at 22:14
  • Some kind of TreeView would make more sense. Look at how https://jsonformatter.curiousconcept.com/ shows it – Charlieface Nov 01 '21 at 23:24
  • @VinsonSack yes, `string[]`, unless you want to make an enum for the `pos` property. It looks like that property has a few known values. In that case you could make it a `PosEnumType[]` instead. – gnud Nov 02 '21 at 21:01
  • A tree view is not a bad idea - but we have in effect two tables, and the 2nd set of 4 columns is a child of the parent row. Worse yet, no values like say PK,FK exist to connect to the two sets of master and child data. However, see my post below - I show a not all that bad way to display this data. We could have perhaps used two nested grids, but a repeater (or datalist), and then using child gridview works quite well. – Albert D. Kallal Nov 03 '21 at 23:37

2 Answers2

1

Ok, if you look close, we have the first two rows, and they are repeating data.

Then the next set of columns is a child table.

In effect, this is what we have:

public class Class1
{
    public string[] sentence { get; set; }
    public string[] sentenceNbr { get; set; }
    public string[][] tokens { get; set; }
    public string[][] pos { get; set; }
    public string[][] ner { get; set; }
    public string[][] lemmas { get; set; }
}

Note how the first two columns are array of string[], but the last 4 columns are in fact a string[] of a string[] (nested array).

Unfortantly, the gridview can't really display that. but, gridview, listview, repeater, datalist - they ALL can take the FIRST two columns.

So, we could droop in a repeater - or a datalist (they both are rather close in what they do - repeat data).

So, say we have this DataList:

I JUST have the first two columns.

        <asp:Datalist ID="MyDataList" runat="server" OnItemDataBound="MyDataList_ItemDataBound">

         <ItemTemplate>

             <div style="float:left">
                 <h3>Sentance</h3>
                <asp:Label ID="Sentence" runat="server" Text='<%# Eval("sentence") %>'
                    Width="300px" 
                   ></asp:Label>
             </div>
             <div style="float:left">
                 <h3>Nbr</h3>
         <asp:Label ID="sentenceNbr" runat="server" Text='<%# Eval("sentenceNbr") %>' 
             Width="80px"></asp:Label>
             </div>
            </ItemTemplate>
        </asp:Datalist>

And now our code to load:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
            LoadMyData();
    }

    void LoadMyData()
    {
        string strJSON = "";
        strJSON = File.ReadAllText(@"c:\test7\udata.txt");

        strJSON = "{ 'myroot' :" + strJSON + "}";
        DataSet MyTables = new DataSet();
        MyTables = JsonConvert.DeserializeObject<DataSet>(strJSON);

        MyDataList.DataSource = MyTables;
        MyDataList.DataBind();

    }

Note how I had to add a "root" to your json data.

Ok, so now we see this:

enter image description here

Hey, not bad at all!!!!

Now the additonal columns - they just don't work with above.

but that means we have to NEST and drop in a grid into the above DataList/Repeater control.

Ok, lets do that. So to above, right below the first two lables, we drop in this:

 <div style="margin-left:25px">
   <asp:GridView ID="GridView2" runat="server" 
       CssClass="table table-condensed"></asp:GridView>
 </div>

Now, we have to fill that up. I am REALLY trying to keep this code down, but I can't think of a better way (where is a linq and lamda expression expert when you need one???).

However, this seems to work:

So we are going to use what is called the data bind event. This is much the same for a gridview, listview, repeater etc. (the beauty of .net is once you learn one, then you know them all).

So, we will get the current data row we are bindings.

Create a fake table for the 4 multi-value columns shove the data into that fake table, and then shove the table into that gridview.

It not TOO much code, but VERY close to the limits of what is practical on SO.

So, this works:

    protected void MyDataList_ItemDataBound(object sender, DataListItemEventArgs e)
    {
        GridView gv = (GridView)e.Item.FindControl("GridView2");
        DataRowView OneDataRow = (DataRowView)e.Item.DataItem;
        
        DataTable rstData = new DataTable();
        rstData.Columns.Add("tokens");
        rstData.Columns.Add("pos");
        rstData.Columns.Add("ner");
        rstData.Columns.Add("lemmas");
        
        for (int ChildRow = 0;ChildRow < ((string[])OneDataRow["tokens"]).Length;ChildRow++) {
            // add new row
            DataRow NewRow = rstData.NewRow();
            foreach(DataColumn myCol in rstData.Columns)
            {
                // add each column value
                NewRow[myCol.ColumnName] = ((string[])OneDataRow[myCol.ColumnName])[ChildRow].ToString();
            }
            rstData.Rows.Add(NewRow);
        }
        gv.DataSource = rstData;
        gv.DataBind();
    }

not too bad, and not too messy.

So, I think you can see what we are doing here.

Output: enter image description here

Albert D. Kallal
  • 42,205
  • 3
  • 34
  • 51
  • Would you still have to add myroot if I am pulling this data automatically – Vinson Sack Nov 04 '21 at 00:52
  • Well, you might not have to add "myroot' (I just picked any name for the table), but if you looking around the web? Every json string I ever seen without any exception starts with a { and ends with }. So you probably might get some json parsers to work without the table name, but you at least need a string that is inside of {} – Albert D. Kallal Nov 04 '21 at 02:15
  • Thank you so much for all your help thus far. I want to use this same logic for a single "sentance" object. Would that be possible still using the data set? – Vinson Sack Nov 04 '21 at 02:25
  • Is the sentance just one, or repeating? (but, yes in both cases). As noted, I used a dataset/datatable/datarow. But that' because I am always thinking in terms of database tables - rows and columns. XML or json to me is always just some table - I never think of them as being different. Since I code out repeaters, datalist and gridview with tables like thinking? Then of course my solutions are thus "tables" in thought and concept. As noted, in place of a dataset, you could consider a list, but I think it is just as much work - often a bit more. Even one value would go to a dataset. – Albert D. Kallal Nov 04 '21 at 02:36
  • Remember, a dataset is collection of tables. So DataSet1.Tables[0].Rows[0]["sentence"] would get you the first one. You can also learn/use json "path" names to pluck out values - It going to depend on what you trying to do in code. So, in above example - I moved things to dataset, since then gridview, or datalist was able to then do a lot of processing and dirty work for me. As noted, which road you pick will much depend on what you needing. I mean, perhaps even a tree view would be cool here!!! (and I not tested if binding the treeview to our dataset in one shot might be the best here. – Albert D. Kallal Nov 04 '21 at 02:40
0

first define your c# class from your json. you can use https://json2csharp.com/

this is your c# class

// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse); 
public class Root
{
    public string sentence { get; set; }
    public string sentenceNbr { get; set; }
    public List<string> tokens { get; set; }
    public List<string> pos { get; set; }
    public List<string> ner { get; set; }
    public List<string> lemmas { get; set; }
}

install Newtonsoft.json for deserialize them.

   var data = JsonConvert.DeserializeObject<List<Root>>(jsonAsText);
   dataGridView1.DataSource = data;
   dataGridView1.Refresh();

this very simple way to show your data.

alirezasejdei
  • 180
  • 2
  • 14
  • 1
    `dataGridView1.Refresh();` probably unnecessary. I don't think you'll get a datagridview to appear like the sample provided from this code.. – Caius Jard Nov 02 '21 at 09:00
  • 1
    You a are correct - gridview or any of the controls can't handle the nested data. With above, the GV will display only the first two rows. And you don't really need to define a class as my above code shows - you can define that class, but it will not get you very far anyway. – Albert D. Kallal Nov 02 '21 at 19:52
  • 1
    On the other hand? What a fantastic question - perfect interview question! How would one display this data. – Albert D. Kallal Nov 02 '21 at 19:53
  • 1
    fyi, generating classes from JSON or XML is built into Visual Studio. Simply create a empty class file, copy the JSON string to the clipboard, then in your empty .cs class file goto Edit > Paste Special > Paste JSON as Classes – zgood Nov 02 '21 at 23:17
  • 1
    Well, even with a class, you still can't get the child data to display in a grid without nesting. And you can't pull the child data (the 4 rows) out of the class without some work anyway. The end result is creating the class don't help. If this was not master->child data, then no problem. On the other had we see that newtonsoft does the SAME job and spits out the column names without even having built a class. But, the child data still a issue. – Albert D. Kallal Nov 03 '21 at 00:48