.NET 4.0; VS 2010.
We're consuming a web service that does not offer a WSDL. The data that is returned is not particularly complicated so we thought we would work with dynamic/anonymous types. Here is an example of the JSON returned from one of the service methods (this string has been verified with JSONLint):
[
{
"value": "AAA"
},
{
"value": "BBB"
},
{
"value": "CCC"
},
{
"value": "DDD"
},
{
"value": "EEE"
},
{
"value": "FFF"
}
]
Tried using:
dynamic respDyn = JsonConvert.DeserializeObject(jsonStringAbove);
In this case, no errors are thrown, but in trying to access the resp variable, the Visual Studio debugger reports "The name 'resp' does not exist in the current context".
Tried LINQ next:
var respLinq = JObject.Parse(jsonStringAbove);
Which results in a runtime error: Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray. Path '', line 1, position 1.
Found this article that recommended different parsing methods depending on the format of the JSON:
if (jsonStringAbove.StartsWith("["))
{
var arr = JArray.Parse(jsonStringAbove);
}
else
{
var obj = JObject.Parse(jsonStringAbove);
}
When var arr = JArray.Parse(jsonStringAbove); is hit, the debugger simply exists the method and returns to the calling procedure. No error is thrown. If the leading and trailing square brackets are removed, another run time error similar to the results in the second example is encountered.
So. Not sure where to turn at this point. Seems like what we're trying to do is very straightforward which make me think I'm missing something blatantly obvious.
Not sure why, but the solution to this was to declare my variables as fields within the class. Variables that were local to the methods I was working with simply did not work. Once declared as class-wide variables, the code behaved as expected. Very odd. I suspect that this problem may be specific to my VS environment and/or solution configuration as it does not appear to be occurring with anyone else. Lucky me.
Related
I am working in Kafka Lenses v2.2.2. I need to filter based on a the value of an object inside an array.
Sample message (redacted for simplicity):
{
"payload": {
"Data": {
"something" : "stuff"
},
"foo": {
"bar": [
{
"id": "8177BE12-F69B-4A51-B12E-976D2AE37487",
"info": "more_data"
},
{
"id": "06A846C5-2138-4107-A5B0-A2FC21B9F32D",
"info": "more_data"
}
]
}
}
In lenses this actually appears as a nested object with a integer properties... 0, 1, etc.
So I've tried this, but it is throwing an error: .0 appears out of place
SELECT *
FROM topic_name
WHERE payload.foo.bar.0.id = "8177BE12-F69B-4A51-B12E-976D2AE37487"
LIMIT 10
I tried wrapping the 0 in double/single quotes as well and that throws a 500 error.
I copied and pasted the UUID from the first message in the topic, so it's definitely there. I also copy and pasted the labels to rule out typos. I am thinking there is some special way to access arrays with nested objects like this, but I'm struggling to find any documentation or videos discussing it.
I can be confident the value is stored in the first array element, but methods that can search all objects would be awesome as well.
The syntax (if you know the array index - as in my initial question) is:
SELECT *
FROM topic_name
WHERE payload.foo.bar[0].id = "8177BE12-F69B-4A51-B12E-976D2AE37487"
LIMIT 10
Though I am still struggling to do this if the array index is unknown and you need to check them all. I'm assuming at this point it's not possible without a series of OR statements in the WHERE clause that checks them all.
I have this json:
{
"$type": "System.Collections.Generic.List<MyType>",
"$values": [
{
"$type": "MyType",
"o": 7.54,
"t": 1619002800000,
"n": 3
},
{
"$type": "MyType",
"o": 7.53,
"t": 1619005140000,
"n": 3
}
]
}
I want to deserialize it back into a List<MyType>. I thought there would be an easy way to do that some thing like this:
var myList = json.FromJson<MyType>();
but that doesn't work.
I have figured out a way to accomplish my goal but it's a bit messy so I was wondering if there's a better way that I'm not aware of. Here's the messy way I came up with:
var myListOfObject = (List<object>)((Dictionary<string, object>)JSON.parse(json))["$values"];
var myTypes = myListOfObject.ConvertAll(x => JSON.stringify(x).FromJson<MyType>());
I'm not necessarily looking for fewer lines of code because 2 isn't anything to complain about. I'm just hoping there is a way that doesn't require all the casting and parsing and rather can accept the json as is and get it back to the type it came from. Maybe there's even a parameter I can set to tell it to validate types during the deserialization since the full type names are in the json.
You should use the same serializer you used to serialize the payload to deserialize it. ServiceStack.Text uses __type to embed its type information, in a different schema so you wont be able to use ServiceStack.Text to automatically deserialize it into the embedded type.
This likely used JSON.NET which you should use instead to deserialize it, otherwise yeah you can use ServiceStack's JS Utils to deserialize arbitrary JSON as you're doing.
Statement of intent:
I'm trying to automate a workflow, moving data periodically from a CSV in Sharepoint into a table in Azure SQL Database. I've gotten so far as 1) Formatting a JSON array, and 2) Creating a SPROC that successfully takes the text of the JSON Array, and imports it into the appropriate table.
Array appears like:
JSON = [{"col1":"col1Data","col2":"col2Data", ...}, <600-some more iterations>]
Invocation of stored procedure in SQL Management Studio looks like:
EXECUTE SprocName #json=N'<text of JSON above>'
===========================================
Problem:
Lack of documentation allowing me to properly format one of the following two SQL Connectors' parameters to link these two statements together:
Both Execute a Query (v2) and Execute a Stored Procedure (v2) require that parameters or query text be provided, but no indication of how said parameters should be formatted.
For example, in terms of executing a stored procedure that takes a single parameter #json, the following text "looks" correct, but results in an error:
"body": "#json=N'+#string(outputs('Convert_Rows_To_Json').body)+'"
Error:
Failed to save logic app UpdateDomainCoverage. The template validation failed: 'The template action 'Execute_stored_procedure_(V2)' at line '1' and column '3148' is not valid: "The template language expression 'json=N'+#string(outputs('Convert_Rows_To_Json').body)+'' is not valid: the string character '=' at position '4' is not expected.".'.
I've tried a number of variations, for both the #json parameter on Execute Stored Procedure, or simply building the query from whole cloth in Execute SQL, to no avail. Suggestions?
Here is sample from Code View of calling a stored procedure with parameter 'from' that takes a datetime value. When you pick the sproc in the Designer it should show all the parameters for you to populate.
"Get_jobs": {
"inputs": {
"body": {
"from": "#{convertFromUtc( variables('SelectTime'), variables('timeZone'), 'yyyy-MM-dd HH:mm:ss')}"
},
"host": {
"connection": {
"name": "#parameters('$connections')['sql_2']['connectionId']"
}
},
"method": "post",
"path": "/datasets/default/procedures/#{encodeURIComponent(encodeURIComponent('[dbo].[GetJobs]'))}"
},
"runAfter": {
"Refresh_data_for_BI": [
"Succeeded"
]
},
"type": "ApiConnection"
},
OK, I've been messing with this on-and-off in between other tasks today, and finally got tired of trying to get it done in the input of the "Execute query".
Brute Force Solution: I added another Javascript step, with the following code:
var input = workflowContext.actions.Convert_Rows_To_Json.outputs.body;
var sqlQuery = 'EXECUTE [ImportDomainCoverage] N\'' + input + '\'';
return sqlQuery;
It's not pretty (one more step), but it works.
Now to see if I can modify things sufficiently to parameterize the table name, rather than needing six steps for each table.
Finally figured out the syntax. Didn't find any documentation, just tried working from one error message to another.
"Pump_data_into_target_table": {
"inputs": {
"body": {
"json": "#{body('Pull_FeedbackItems_from_source').ResultSets['Table1']}"
},
"headers": {
"Content-Type": "application/json"
},
"host": {
"connection": {
"name": "#parameters('$connections')['sql_2']['connectionId']"
}
},
"method": "post",
"path": "/v2/datasets/#{encodeURIComponent(encodeURIComponent('servername.database.windows.net'))},#{encodeURIComponent(encodeURIComponent('dbname'))}/procedures/#{encodeURIComponent(encodeURIComponent('sprocname'))}"
},
"runAfter": {
"Pull_FeedbackItems_from_Source": [
"Succeeded"
]
},
"type": "ApiConnection"
}
The fundamental answer to my question was: provide the parameter/value pairs as a JSON object. See the value of the "body" element in the listing above. For this to work, though, one also has to enter the "headers" element, which I didn't even see documented on the API call. Was led to that by an error message stating that the content type was plain text, when it was clearly json.
I'm trying to use Katalon Studio for some webservice automation. I have been following this guide on how to parse returned Json body using jsonslurper.
https://docs.katalon.com/katalon-studio/tutorials/parse_json_responses.html
Everything is working fine as described in the guide. I wanted to see if I can use junit asserts, specifically the assertEquals() for better error text.
Given we have this
import groovy.json.JsonSlurper
String jsonString = {"menu": {
"id": "file",
"tools": {
"actions": [
{"id": "new", "title": "New File"},
{"id": "open", "title": "Open File"},
{"id": "close", "title": "Close File"}
],
"errors": []
}}}
JsonSlurper slurper = new JsonSlurper()
Map parsedJson = slurper.parseText(jsonString)
def array1 = parsedJson.menu.tools.actions
String onlickValue1 = ""
for(def member : array1) {
assertEquals("Open File", member.title)
break
}
What I'm having trouble with, is that my assert will thrown an error when comparing the very first title element it encounters (which is "New File").
What I intend is to loop through all the elements in the array and assert my expected value against all of them. If my expected value doesn't exist, then I'd fail.
I feel like I'm missing something, because we've done something similar in the past with java, but I just can't see it here.
So I figured out the problem was my inexperience/ignorance. When looking for solutions online I failed to understand with absolute certainty what the code I'm trying to implement is doing. I was using a for.each loop to assert elements in the array against my expected value. Which of course was failing, correctly, for every element that didn't match my expected value. So I made it work by adding an if statement as below:
String expectedValue = ''
for(def member : array1) {
if (member.title=="Open File")
{
expectedValue = member.title
}
break
}
assertEquals("Open File", member.title)
Also a simpler way I discovered is to use assertJ in the following way
assertThat(member).contains("Open File")
I understand there are better solutions to achieve what I'm trying to do. But for purposes of this question I considered it solved.
I am trying to save (RavenDB build 960) the names and values of form data items passed into a Nancy Module via its built in Request.Form.
If I save a straightforward instance of a dynamic object (with test properties and values) then everything works and both the property names and values are saved. However, if I use Nancy's Request.Form then only the dynamic property names are saved.
I understand that I will have to deal with further issues to do with restoring the correct types when retrieving the dynamic data (RavenJObjects etc) but for now, I want to solve the problem of saving the dynamic names / values in the first place.
Here is the entire test request and code:
Fiddler Request (PUT)
Nancy Module
Put["/report/{name}/add"] = parameters =>
{
reportService.AddTestDynamic(Db, parameters.name, Request.Form);
return HttpStatusCode.Created;
};
Service
public void AddTestDynamic(IDocumentSession db, string name, dynamic data)
{
var testDynamic = new TestDynamic
{
Name = name,
Data = data
};
db.Store(testDynamic);
db.SaveChanges();
}
TestDynamic Class
public class TestDynamic
{
public string Name;
public dynamic Data;
}
Dynamic contents of Request.Form at runtime
Resulting RavenDB Document
{
"Name": "test",
"Data": [
"username",
"age"
]
}
Note: The type of the Request.Form is Nancy.DynamicDictionary. I think this may be the problem since it inherits from IEnumerable<string> and not the expected IEnumerable<string, object>. I think that RavenDB is enumerating the DynamicDictionary and only getting back the dynamic member-names rather than the member name / value pairs.
Can anybody tell me how or whether I can treat the Request.Form as a dynamic object with respect to saving it to RavenDB? If possible I want to avoid any hand-crafted enumeration of DynamicDictionary to build a dynamic instance so that RavenDB can serialise correctly.
Thank You
Edit 1 #Ayende
The DynamicDictionary appears to implement the GetDynamicMemberNames() method:
Taking a look at the code on GitHub reveals the following implementation:
public override IEnumerable<string> GetDynamicMemberNames()
{
return dictionary.Keys;
}
Is this what you would expect to see here?
Edit 2 #TheCodeJunkie
Thanks for the code update. To test this I have:
Created a local clone of the NancyFx/Nancy master branch from
GitHub
Added the Nancy.csproj to my solution and referenced the project
Run the same test as above
RavenDB Document from new DynamicDictionary
{
"Name": "test",
"Data": {
"$type": "Nancy.DynamicDictionary, Nancy",
"username": {},
"age": {}
}
}
You can see that the resulting document is an improvement. The DynamicDictionary type information is now being correctly picked up by RavenDB and whilst the dynamic property-names are correctly serialized, unfortunately the dynamic property-values are not.
The image below shows the new look DynamicDictionary in action. It all looks fine to me, the new Dictionary interface is clearly visible. The only thing I noticed was that the dynamic 'Results view' (as opposed to the 'Dynamic view') in the debugger, shows just the property-names and not their values. The 'Dynamic view' shows both as before (see image above).
Contents of DynamicDictionary at run time
biofractal,
The problem is the DynamicDictionary, in JSON, types can be either objects or lists ,they can't be both.
And for dynamic object serialization, we rely on the implementation of GetDynamicMemberNames() to get the properties, and I assume that is isn't there.