Extra parenthesis '{' is coming in JSON output from SQL server - c#

I am new in JSON & want to generate JSON output directly from SQL server.
But while calling SP from C#.net, i am getting an extra '{' bracket in the start.
And while trying to store the JSON into SQL, it is giving error saying it is not valid JSON.
Below is my code from Stored procedure.
Create PROCEDURE [dbo].[GetData]
As
BEGIN
SELECT Trucknumber,
(
select * from TruckDetails
where TruckId = Truckmaster.TruckId
FOR JSON AUTO
) AS CompData
FROM Truckmaster
Where TruckNumber = 'HP09A3487'
FOR JSON AUTO
END
C# Code------------------------------------
public StringBuilder RunStoredProcedureWithString(string strSPName)
{
StringBuilder jsonResult = new StringBuilder();
var queryWithForJson = "exec " + strSPName;
var conn = new SqlConnection(GenerateConnectionString(_DBName));
var cmdNew = new SqlCommand(queryWithForJson, conn);
conn.Open();
var reader = cmdNew.ExecuteReader();
if (!reader.HasRows)
{
jsonResult.Append("[]");
}
else
{
while (reader.Read())
{
jsonResult.Append(reader.GetValue(0).ToString());
}
}
return jsonResult;
}
Calling Stored procedure.
var strJson = objDBSupport.RunStoredProcedureWithString("GetData");
if(strJson.Length > 0)
{
//Success
}
Output desired & also coming in SQL.
[{"Trucknumber":"HP09A3487","CompData":[{"TruckId":37886,"CompNo":1,"Capacity":6000},{"TruckId":37886,"CompNo":2,"Capacity":4000},{"TruckId":37886,"CompNo":3,"Capacity":3000},{"TruckId":37886,"CompNo":4,"Capacity":5000}]}]
Output coming in C# with extra '{', which is invalid for inserting into another Table.
{[{"Trucknumber":"HP09A3487","CompData":[{"TruckId":37886,"CompNo":1,"Capacity":6000},{"TruckId":37886,"CompNo":2,"Capacity":4000},{"TruckId":37886,"CompNo":3,"Capacity":3000},{"TruckId":37886,"CompNo":4,"Capacity":5000}]}]}
Any suggestion?

I reproduced your setup but couldn't reproduce any problem. I suspect you're doing this to look at your data:
Visual Studio is adding the outermost { } purely for display purposes in the debugger. They aren't part of your data. Here my data definitely has no {} in:
It does this to every object you inspect:
A StringBuilder has an overriden ToString that shows the contents instead of the type name (the default behavior of object's ToString), so the contents of the SB appear in the debugger tooltip, inside of { }. Because { looks like JSON you're getting confused..
See the difference in the immediate window, in "object representation" versus "string representation"
Note, those \ before the " in the string rep aren't in your data either; they're added when visualizing strings to make it easier to paste a string into code as a literal
If you want to truly look at your data without these manipulations, put it into a string and then click the magifying glass in the tooltip of the StringBuilder (there are various options for visualizing it):
You see the string in a textbox, as you would if you wrote it to a file and opened it in Notepad
If this visualization confusion isn't the actual problem, post a screenshot of how youre determining your data has extra {} so we can see your thought process and work back from it

Related

How to output or format data as a table in LinqPad?

I was wandering how to output (or format) data in LinqPad as a table. I know that for some data types LinqPad generates tables. For example, it can dump 2D-arrays:
After googling, I didn't find a direct answer. Let's say I want to create a two-columns table for DateOnly.ToString(...):
format string
formatted value
toString()
27/12/2022
ToString(DD.MM.YYYY)
DD.12.YYYY
ToString(dd.MM.yyyy)
27.12.2022
How can I build such a table in LinqPad?
It seems like the correct solution is to use a collection of anonymous types to build tables.
The following code:
DateOnly dateOnlyNow = DateOnly.FromDateTime(DateTime.Now);
var output = new[] {
new { method = "ToString()", value = dateOnlyNow.ToString() },
new { method = "ToString(DD.MM.YYYY)", value = dateOnlyNow.ToString("DD.MM.YYYY") },
new { method = "ToString(dd.MM.yyyy)", value = dateOnlyNow.ToString("dd.MM.yyyy") },
};
output.Dump();
produces the expected result:

How to modify value of a property in a json text in C#?

I have a service that receives some data with random structure in JSON format and persists these data in a document-based database. I have no control over sending services and there are some sensitive data that should not be persisted in my database. I just want to filter those fields and replace their values with some masking characters. Is there any built-in solution in dotnet5 without adding third-party libraries like json.net or Json Newtonsoft?
I cannot deserialize incoming JSON to a static typed class because I have no idea about how many different types are sent from other services. An also working with reflection and dynamic typing has its own performance penalties.
if you don't have any way to change in code, you can handle it in sql at the time of json insert.
like:
CREATE PROCEDURE dbo.InsertJson(#doc NVARCHAR(MAX))
AS BEGIN
INSERT INTO Person (id, name, surname, age)
SELECT id, firstNAme, lastName, age
FROM OPENJSON(#json)
WITH (id int, firstName nvarchar(50), lastName nvarchar(50),age int)
END
I finally used regex to find and make masked using this code snipped.
private string GetMaskedJson(string json, string[] fieldNames)
{
if (string.IsNullOrEmpty(json)) return string.Empty;
foreach (var field in fieldNames)
{
var pattern = "\""+field+"\"\\s*:\\s*(\"?)(.+?)((\")|\\,|\\})";
var maskedValue = "===MASKED VALUE===";
var r = new Regex(pattern);
var k = r.GetGroupNames();
var t = Regex.Matches(json, pattern);
//R
foreach (var ma in t.Reverse().ToList())
{
var masked = maskedValue;
if (string.IsNullOrWhiteSpace(ma.Groups[1].Value)) masked = $"\"{maskedValue}\"";
json = json.Remove(ma.Groups[2].Index, ma.Groups[2].Length)
.Insert(ma.Groups[2].Index, masked);
}
}
return json;
}

Get Object Value from JSON Object in C#

Here is the what I am trying to achieve here:
Get a list of file names and IDs from the database
Search for those files on a network path
Store any IDs of the files not found
Search for that ID in a second database
Search for those files on a network path
List all ID's where files are not found on either location.
The issue I an encountering is trying to use the file names from results I have collected.
When running the code, the raw JSON data collected from the database gets displayed, however when trying to list just the file names I get nothing (not even an error)
Any ideas on how to fix this and list the file names in a way that would also let me search for them later? Or is there a better way to do this?
Also note: due to the version of SQL server being used i have to use FOR XML as FOR JSON is not compatible.
EDIT: Using code provided by Prany I am now able to output just the Audio File Name, but it I try and also output the sCallId I get a duplication Issue (see below output):
Getting Calls
2016\03\30\300320161614210106361-00000934412405.asf--84377-3668343241-514513
2016\03\30\300320161614210106361-00000934412405.asf--84385-3668343557-255773
2016\03\30\300320161614210106361-00000934412405.asf--84392-3668344445-516453
2016\03\30\300320161614210106361-00000934412405.asf--85000-3668749568-733799
2016\03\30\300320161614210106361-00000934412405.asf--85604-3668872399-722313
2016\03\30\300320161620220106362-00000934052048.asf--84377-3668343241-514513
2016\03\30\300320161620220106362-00000934052048.asf--84385-3668343557-255773
2016\03\30\300320161620220106362-00000934052048.asf--84392-3668344445-516453
2016\03\30\300320161620220106362-00000934052048.asf--85000-3668749568-733799
2016\03\30\300320161620220106362-00000934052048.asf--85604-3668872399-722313
2016\03\30\300320161634220106363-00000933211384.asf--84377-3668343241-514513
2016\03\30\300320161634220106363-00000933211384.asf--84385-3668343557-255773
2016\03\30\300320161634220106363-00000933211384.asf--84392-3668344445-516453
2016\03\30\300320161634220106363-00000933211384.asf--85000-3668749568-733799
2016\03\30\300320161634220106363-00000933211384.asf--85604-3668872399-722313
2016\04\04\040420160908190106389-00000527974488.asf--84377-3668343241-514513
2016\04\04\040420160908190106389-00000527974488.asf--84385-3668343557-255773
2016\04\04\040420160908190106389-00000527974488.asf--84392-3668344445-516453
2016\04\04\040420160908190106389-00000527974488.asf--85000-3668749568-733799
2016\04\04\040420160908190106389-00000527974488.asf--85604-3668872399-722313
2016\04\05\050420161913220106406-00000405271715.asf--84377-3668343241-514513
2016\04\05\050420161913220106406-00000405271715.asf--84385-3668343557-255773
2016\04\05\050420161913220106406-00000405271715.asf--84392-3668344445-516453
2016\04\05\050420161913220106406-00000405271715.asf--85000-3668749568-733799
2016\04\05\050420161913220106406-00000405271715.asf--85604-3668872399-722313
Below Is the code I am currently using to try and do this.
//Run the SQL and wrap the output in results tags to fix Multiple Root Elements error.
string liveXML = "<results>" + cmd2.ExecuteScalar().ToString() + "</results>";
//Create new XML Document
XmlDocument LiveDoc = new XmlDocument();
LiveDoc.LoadXml(liveXML);
//Conver XML to JSON
sjsonLive = JsonConvert.SerializeXmlNode(LiveDoc);
//Output RAW JSON
txtOut.AppendText("\r\n" + sjsonLive);
//Parse JSON into an Array
var files = JObject.Parse(sjsonLive);
//We want to run this values in a files seach, but for now let's print it to txtOut
foreach (var f in files.SelectTokens("$..calls..#audioFileName"))
foreach (var c in files.SelectTokens("$..calls..#sCallID"))
{
txtOut.AppendText("\r\n" + f.ToString() + " - " + c.ToString());
//Conduct File Search Here...
}
Example JSON Data:
{
"results": {
"calls": [{
"#audioFileName": "2016\\03\\30\\300320161614210106361-00000934412405.asf",
"#sCallID": "84377-3668343241-514513"
}, {
"#audioFileName": "2016\\03\\30\\300320161620220106362-00000934052048.asf",
"#sCallID": "84385-3668343557-255773"
}, {
"#audioFileName": "2016\\03\\30\\300320161634220106363-00000933211384.asf",
"#sCallID": "84392-3668344445-516453"
}, {
"#audioFileName": "2016\\04\\04\\040420160908190106389-00000527974488.asf",
"#sCallID": "85000-3668749568-733799"
}, {
"#audioFileName": "2016\\04\\05\\050420161913220106406-00000405271715.asf",
"#sCallID": "85604-3668872399-722313"
}
]
}
}
Edit:
You can use below token selector
files.SelectTokens("$..calls..#audioFileName")
edit 2:
var calls = files.SelectTokens("$..calls..#sCallID").ToList();
var audiofiles = files.SelectTokens("$..calls..#audioFileName").ToList();
for (int i = 0; i <= audiofiles.Count; i++)
{
//Conduct File search
if (true)
{
//access by index like audiofiles[i] and append to the query
}
else
{
//access calls by index like calls[i] and append to the query
}
}

Converting string builder to json string

I want get data from a SQL Server database using C#. I am trying to get a json string from string builder. I try like this:
public string GetData()
{
using (SqlConnection con = new SqlConnection(this.Connection))
{
con.Open();
SqlCommand command = new SqlCommand("Select TITLE, DURATION, STATUS, TYPE from PROJECTS ", con);
StringBuilder sb = new StringBuilder();
sb.Append("{");
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
var k = reader.GetString(3);
if (k == "M")
{
sb.Append("main");
sb.Append("{");
sb.Append("sub:[]");
sb.Append("Tittle:");
sb.AppendFormat("{0}", reader["TITTLE"]);
}
if (k == "S")
{
sb.Append("sub");
sb.Append("{");
sb.Append("task:[]");
sb.Append("Tittle:");
sb.AppendFormat("{0}", reader["TITTLE"]);
}
if (k == "T")
{
sb.Append("task");
sb.Append("{");
sb.Append("Tittle:");
sb.AppendFormat("{0}", reader["TITTLE"]);
}
};
}
sb.Append("}");
sb.AppendLine();
return sb.ToString();
}
}
Now I get the string like
sb = {{main{sub:[]Tittle:newsub{task:[]Tittle:new1task{Tittle:new2}
but my required string is like:
[{"main":{"sub":[{"task":[{"tittle":"new2""}],"tittle":"new1","}],"tittle":"new","}}]
means: my main title is new and sub tittle new1 and task title new2. What change do I need to do to my code to get my required json string??
There are some problems with your code:
To be a valid JSON string, property names have to be enclosed with double quotes ", so you have to do something like sb.Append("\"main\"");
Every property name has to be followed by a colon :, so you have to do something like sb.Append("\"main\": ");
You are dealing with nested structures, arrays containing objects, which themselves contain arrays ... You can only add the closing bracket ] of an array, after all its items have been added. So do something like
sb.Append("\"sub\": [");
//here you add the subitems in a loop
sb.Append("]");
To be able to do so, you will have to keep track of what structures you have open in the moment and you will have your query to return the rows in the exact required order (ie first the main titles, then the first contained sub, then the tasks contained in that sub, then the second sub, then the tasks ...)
You also never close your higher level open braces {. Same applies here as with arrays. If you add an object, you have to add all its content and then add a closing } brace.
Generally, I would suggest not to create JSON by yourself but use a framework like JSON.net. You can build up your collections as you need them and then call the frameworks serialization method which will generate a valid json string.

Convert to JSON Array, not regular object

I have used this utility http://www.convertcsv.com/csv-to-json.htm to format tables of data. It has the wonderful option of allowing you to convert to JSON or to a JSON Array. That JSON Array is what I want. When I use utilities like JSON.Net to serialize, they give me the standard JSON format. I don't want that - I just want arrays, so I can basically reproduce a table layout in my javascript.
Here is sample table data structure
column1 column2 column3
c1r1 c2r1 c3r1
c1r2 c2r2 c3r2
c1r3 c2r3 c3r3
I want it to look like this when serialized:
[[c1r1,c2r1,c3r1],
[c1r2,c2r2,c3r2],
[c1r3,c2r3,c3r3]]
But the standard serialization method with a utility like JSON.net would be
[
{
column1:c1r1,
column2:c2r1,
column3:c3r1
},
{
column1:c1r2,
column2:c2r2,
column3:c3r2
},
{
column1:c1r3,
column2:c2r3,
column3:c3r3
}
]
My question is, does anyone know of a way of stripping out column names and just making it like the simple 2d array I have shown?
Data structure is an IEnumerable taken directly from a sql command db.Query("SELECT * FROM my_table").
Note: I want to have a generic function that can do this - I know how to do this for just one thing, but the project I'm working on needs it done for many in the same way. I tried to write my own method to do it, but it didn't work because of limitations that c# has.
public static string fromListToJSONArray(IEnumerable<Object> listToUse, string[] fieldNames)
{
string JSONString = "[";
foreach (var item in listToUse)
{
JSONString += item[fieldName[0]]; //This is the line you can't do in c#!! Don't know how to go around this.
}
JSONString += "]";
return JSONString;
}
What you are trying to output is an array of arrays. Starting with an enumerable or records (in your case it looks like it has columns called column1, column2, column3).
If the query will produce a know result set you can simple convert each row to an array before converting to JSON.
var qry = /*Enumerable of some database query*/
return from rec in wry
select new string[]
{
rec.column1,
rec.column2,
rec.column3
};
I think you're asking something similar to this.
You'll have to assemble the array manually from the json.
I found the solution - IEnumerable type needed to be "dynamic". From there it is pretty simple
public static string fromListToJSONArray(IEnumerable<dynamic> listToUse)
{
string JSONString = "[";
foreach (var item in listToUse)
{
if(isFirst == false)
JSONString += ",";
else
isFirst = false;
JSONString += "\"" + item[0] + "\"";
}
JSONString += "]";
return JSONString;
JSONString += "]";
return JSONString;
}

Categories

Resources