Converting string builder to json string - c#

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.

Related

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

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

How to properly get index of a custom class list <CustomClass> and check if the list value in the selected index is equal to int [c#]

I have created a custom class list which is filled from the result of a query. Specifically, me query returns (int, timestamp) 2 columns -> 2 values.
public class SucessfulCompletion
{
public int Result;
public string Timestampvalue;
public SucessfulCompletion(int result, string timestamp) => (Result, Timestampvalue) = (result, timestamp);
}
public List<SucessfulCompletion> SuccessfulCalculationsTimestamp(string connectionstring)
{
List<SucessfulCompletion> QueryListResult = new List<SucessfulCompletion>();
using (SqlConnection sqlConnection = new SqlConnection(connectionstring))
{
var query_table_timestamp = (
#"SELECT CASE
WHEN t.STATUS = 'SUCCESS' AND t.STATUS_DESCRIPTION = 'ALL QUERIES COMPLETED SUCCESSFULLY' THEN 1
ELSE 0
END SuccessfulCompletion, t.TIMESTAMP
FROM (SELECT TOP (1) l.TIMESTAMP, l.STATUS, l.STATUS_DESCRIPTION
FROM LOG_DETAILS l
ORDER BY 1 DESC) t");
sqlConnection.Open();
using (SqlCommand sqlCommand = new SqlCommand(query_table_timestamp, sqlConnection))
{
using (SqlDataReader reader = sqlCommand.ExecuteReader())
{
while (reader.Read())
{
QueryListResult.AddRange(new List<SucessfulCompletion>
{
new SucessfulCompletion(reader.GetInt32(0), reader.GetDateTime(1).ToString())
});
}
reader.Close();
}
sqlCommand.Cancel();
}
}
return QueryListResult;
}
The code to create the custom class list was taken from this SO question
So the QueryListResult would be like [1, "2020-10-04 HH:MM:SS"]
Now I want to make an if statement to check if the first index of the QueryListResult is ether 0 or 1.
List<SucessfulCompletion> reportsucessfulcompletion = new List<SucessfulCompletion>();
reportsucessfulcompletion = SuccessfulCalculationsTimestamp(SQLServerConnectionDetails());
if (reportsucessfulcompletion[0]=1) //my problem is here
{
//Enable is only if successful
PreviewCalculationsButton.IsEnabled = true;
PreviewReportButton.IsEnabled = true;
//add textbox of success
SQLSuccessfulTextCalculations.Text = String.Format("Completed On: {0}", reportsucessfulcompletion[1]);
}
else
{
//add textbox of fail
SQLFailedTextCalculations.Text = String.Format("Failed On: {0}", reportsucessfulcompletion[1]);
}
In the if statement I get an error
Cannot implicitly convert type 'int' to 'TestEnvironment.MainWindow.SucessfulCompletion'
I know it may be a silly question for someone experienced with C# but I am a newbie, so I would appreciate your help. Please inform me in the comments if the theme of the question is a duplicate one I will close the question.
You're comparing an jnstance of your class to a number.
These are different things.
And one equal sign sets rather than compares
Try
If ( reportsucessfulcompletion[0].Result == 1)
You should make these properties rather than variables.
I also recommend Dapper as a "micro" orm very close to the ado metal, but which saves a fair bit of coding whilst implementing best practice for you.

How can I use individual words from user input into a SQL 'WHERE CONTANS' query in C#?

This is my first asked question on here, and I can't find the answer so apologies if I've missed it somewhere.
I'm currently building a search function in a HR Portal that I'm developing (ASP.NET MVC), and while I've managed to get it all set up to query the SQL Server database I've created, it currently works using a LIKE query, e.g.:
public List<Detail> Search(List<string> Information)
{
StringBuilder Buildsql = new StringBuilder();
Buildsql.Append("select * from UH_QA.dbo.Answers where ");
foreach (string value in Information)
{
Buildsql.AppendFormat("(Question like '%{0}%') and ", value);
}
string datasql = Buildsql.ToString(0, Buildsql.Length - 5);
return QueryList(datasql);
}
But in order to make sure the search function is fool proof, I want to use a query like this:
SELECT *
FROM UH_QA.dbo.Answers
WHERE CONTAINS(Question, '"Where" OR "do" OR "I" OR "put" OR "my" OR "phone"')
Any advice on how I might be able to go about changing what I've already got to split the string input by the user and then insert the individual words into the query?
My thoughts where to use value.Split(' ') to split the string by whitespace, but I'm open to suggestions.
You would achieve it by:
Changing the signature of QueryList so it allows an variable number of parameters.
Changing your foreach block to both populate an list of parameters and build the SQL. Do note that this would get rid of the SQL injection vulnerabilities.
In your QueryList method add the parameters to the command.
public List<Detail> Search(List<string> Information)
{
StringBuilder Buildsql = new StringBuilder();
Buildsql.Append("select * from UH_QA.dbo.Answers where ");
List<SqlParameter> queryParameters = new List<SqlParameter>();
for (int i = 0; i < Information.Count; i++)
{
if (!string.IsNullOrEmpty(Information[i]))
{
queryParameters.Add(new SqlParameter("#p" + i.ToString(), SqlDbType.VarChar) {
Value = Information[i]
});
if (i > 0)
{
Buildsql.Append(" OR ");
}
Buildsql.AppendFormat("Question like '%' + #p{0} + '%'", i);
}
}
return QueryList(datasql, queryParameters);
}
public List<Detail> QueryList(string query, IEnumerable<SqlParameter> parameters) {
// Do use the `parameters` when accessing the database
}

SQL server truncates data returned as JSON [duplicate]

I am using AZURE SQL (SQL Server 2016) and creating a query to give me output in JSON object. I am adding FOR JSON PATH at the end of query.
When I execute the procedure without adding FOR JSON PATH to the query, I get 244 rows (no of records in my table); but when I execute the procedure by adding FOR JSON PATH I get message 33 rows and also I get JSON object which is truncated.
I tested this with different types of queries including simple query selecting only 10 columns, but I always get less number of rows with FOR JSON PATH and JSON object truncated at the end.
Here is my query
SELECT
[Id]
,[countryCode]
,[CountryName]
,[FIPS]
,[ISO1]
,[ISO2]
,[ISONo]
,[capital]
,[region]
,[currency]
,[currencyCode]
,[population]
,[timeZone]
,[timeZoneCode]
,[ISDCode]
,[currencySymbol]
FROM
[dbo].[countryDB]
Above query returns 2 rows.
And I use following query to get output in JSON
SELECT
[Id]
,[countryCode]
,[CountryName]
,[FIPS]
,[ISO1]
,[ISO2]
,[ISONo]
,[capital]
,[region]
,[currency]
,[currencyCode]
,[population]
,[timeZone]
,[timeZoneCode]
,[ISDCode]
,[currencySymbol]
FROM
[dbo].[countryDB]
FOR JSON PATH
Above query returns 33 rows and output is
[{"Id":1,"countryCode":"AD","CountryName":"Andorra","FIPS":"AN","ISO1":"AD","ISO2":"AND","ISONo":20,"capital":"Andorra la Vella","region":"Europe","currency":"Euro","currencyCode":"EUR","population":67627,"timeZone":2.00,"timeZoneCode":"DST","ISDCode":"+376"},{"Id":2,"countryCode":"AE","CountryName":"United Arab Emirates","FIPS":"AE","ISO1":"AE","ISO2":"ARE","ISONo":784,"capital":"Abu Dhabi","region":"Middle East","currency":"UAE Dirham","currencyCode":"AED","population":2407460,"timeZone":4.00,"timeZoneCode":"STD","ISDCode":"+971"},{"Id":3,"countryCode":"AF","CountryName":"Afghanistan","FIPS":"AF","ISO1":"AF","ISO2":"AFG","ISONo":4,"capital":"Kabul","region":"Asia","currency":"Afghani","currencyCode":"AFA","population":26813057,"timeZone":4.50,"timeZoneCode":"STD","ISDCode":"+93"},{"Id":4,"countryCode":"AG","CountryName":"Antigua and Barbuda","FIPS":"AC","ISO1":"AG","ISO2":"ATG","ISONo":28,"capital":"Saint Johns","region":"Central America and the Caribbean","currency":"East Caribbean Dollar","currencyCode":"205","population":66970,"timeZone":-4.00,"timeZoneCode":"STD","ISDCode":"+1"},{"Id":5,"countryCode":"AI","CountryName":"Anguilla","FIPS":"AV","ISO1":"AI","ISO2":"AIA","ISONo":660,"capital":"The Valley","region":"Central America and the Caribbean","currency":"East Caribbean Dollar","currencyCode":"205","population":12132,"timeZone":-4.00,"timeZoneCode":"STD","ISDCode":"+1"},{"Id":6,"countryCode":"AL","CountryName":"Albania","FIPS":"AL","ISO1":"AL","ISO2":"ALB","ISONo":8,"capital":"Tirana","region":"Europe","currency":"Lek","currencyCode":"ALL","population":3510484,"timeZone":2.00,"timeZoneCode":"DST","ISDCode":"+355"},{"Id":7,"countryCode":"AM","CountryName":"Armenia","FIPS":"AM","ISO1":"AM","ISO2":"ARM","ISONo":51,"capital":"Yerevan","region":"Commonwealth of Independent States","currency":"Armenian Dram","currencyCode":"AMD","population":3336100,"timeZone":5.00,"timeZoneCode":"DST","ISDCode":"+374"},{"Id":8,"countryCode":"AN","CountryName":"Netherlands Antilles","FIPS":"NT","ISO1":"AN","ISO2":
I am trying to get output directly in JSON
When FOR JSON queries are returned to the client, the JSON text is returned as a single-column result set. The JSON is broken into fixed-length strings and sent over multiple rows.
It's really hard to see this properly in SSMS, as SSMS concatenates the results for you in "Results to Grid", and truncates each row in "Results to Text".
Why? Dunno. My guess is that only .NET clients know how to efficiently read large streams from SQL Server, and 99% of the time users will still just buffer the whole object. Breaking the JSON over multiple rows gives clients a simple API to read the data incrementally. And in .NET the fact that the de facto standard JSON library is not in the BCL means that SqlClient can't really have a first-class JSON API.
Anyway, from C#, you can use something like this to read the results:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class SqlJSONReader: TextReader
{
SqlDataReader rdr;
string currentLine = "";
int currentPos = 0;
public SqlJSONReader(SqlDataReader rdr)
{
this.rdr = rdr;
}
public override int Peek()
{
return GetChar(false);
}
public override int Read()
{
return GetChar(true);
}
public int GetChar(bool Advance)
{
while (currentLine.Length == currentPos)
{
if (!rdr.Read())
{
return -1;
}
currentLine = rdr.GetString(0);
currentPos = 0;
}
int rv = (int)currentLine[currentPos];
if (Advance) currentPos += 1;
return rv;
}
public override void Close()
{
rdr.Close();
}
}
class Program
{
static void Main(string[] args)
{
using (var con = new SqlConnection("server=.;database=master;Integrated Security=true"))
{
con.Open();
var sql = #"
select o.object_id as [obj.Id], replicate('n', 2000) as [obj.foo], c.name as [obj.col.name]
from sys.objects o
join sys.columns c
on c.object_id = o.object_id
for json path;
"
;
var cmd = new SqlCommand(sql, con);
var sr = new StringBuilder();
using (var rdr = cmd.ExecuteReader())
{
using (var tr = new SqlJSONReader(rdr))
{
using (var jr = new Newtonsoft.Json.JsonTextReader(tr))
{
while (jr.Read())
{
Console.WriteLine($" {jr.TokenType} : {jr.Value}");
}
}
}
}
Console.WriteLine(sr.ToString());
}
}
}
}
With thanks to #David Browne. I found I had to use 'print' instead of 'select'
declare #json varchar(max) = (SELECT * FROM dbo.AppSettings FOR JSON AUTO)
print #json
Separation of Concerns dictates returning a string and parsing the JSON separately. The below snippet can be used with no dependency on JSON.net which can be be used separately or a different Json Deserializer can be used (e.g. the one built into RestSharp) and does not require the SqlJSONReader class.
try {
using (var conn = new SqlConnection(connectionString))
using (var cmd = new SqlCommand(sql, conn)) {
await conn.OpenAsync();
logger.LogInformation("SQL:" + sql);
var rdr = await cmd.ExecuteReaderAsync().ConfigureAwait(false);
var result = "";
var moreRows = rdr.HasRows;
while (moreRows) {
moreRows = await rdr.ReadAsync();
if (moreRows) result += rdr.GetString(0);
}
return result;
}
}
catch (Exception ex) {
//logger.LogError($"Error accessing Db:{ex}");
return null;
}
sql Query result of for json path is a long string that is divided to multi column or block
something like this statement: "i want to get result of for json path"
"i want"+
" to ge"+
"t resu"+
"lt of "+
"for js"+
"on path"
each block is equal to max size of column in sql
so just get them all to a list
public IHttpActionResult GetAdvertises()
{
var rEQUEST = db.Database.SqlQuery<string>("SELECT
ID,CITY_NAME,JSON_QUERY(ALBUM) AS ALBUM FOR JSON PATH").ToList();
foreach(string req in rEQUEST)
{
HttpContext.Current.Response.Write(req);
}
return Ok();
}
If your query returned more then 2033 charters then there will be rows. Each row contains 2033 charters data another row contains remaining data. So you need to merge to get actual json. As seen below code sample.
dynamic jsonReturned = unitOfWork
.Database
.FetchProc<string>("storedProcedureGetSaleData", new { ProductId = productId });
if (Enumerable.Count(jsonReturned) == 0)
{
return null;
}
dynamic combinedJson = "";
foreach (var resultJsonRow in jsonReturned)
{
combinedJson += resultJsonRow;
}
return combinedJsonResult;

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