Linq object properties disappear after serialization - c#

I'm trying to understand the included code snippets behavior, it doesn't work as I'd expect. Here is what I'm doing:
Serialize a LINQ object to JSON
Deserialize the JSON at (1) back in its initial type
Validate that each property are still in the Object
I serialize the object from (2) back to JSON
Print the json from (4) and visually inspect it
My issue is that at (5), any LINQ entity that is a property of the main LINQ object is gone, even tho it was still there when on validation at (3). All my LINQ classes are LINQ to SQL. I am using Newtons JSON.Net library. I tried the same type of logic with non-linq object and the defect seen at (5) doesn't occur.
var infoId = Guid.NewGuid();
var alertId = Guid.NewGuid();
var info = new Info();
info.InfoId = infoId;
var alert = new Alert();
alert.AlertId = alertId;
alert.Infos.Add(info);
var json = JsonConvert.SerializeObject(alert);
Debug.WriteLine(json); //All looking good, nothing missing
var deserializedObject = JsonConvert.DeserializeObject<Alert>(json);
Assert.AreEqual(alertId, deserializedObject.AlertId); //Assert is valid
Assert.AreEqual(infoId, deserializedObject.Infos[0].InfoId); //Assert is valid
var json2 = JsonConvert.SerializeObject(deserializedObject);
Debug.WriteLine(json2); //Infos is gone
Update:
I have done some debbugging, and in the Serialization of deserializedObject
var json2 = JsonConvert.SerializeObject(deserializedObject);
I see the following when reaching the Info Serialization step (next code snipped):
this.serializing is true
this._Infos.HasLoadedOrAssignedValues is false
The get return null is called. (get returns the null value)
If I put a breakpoint, and put my cursor on the return this._Infos I actually see the object that It should return ...
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Alert_Info", Storage="_Infos", ThisKey="AlertId", OtherKey="AlertId")]
[global::System.Runtime.Serialization.DataMemberAttribute(Order=15, EmitDefaultValue=false)]
public EntitySet<Info> Infos
{
get
{
if ((this.serializing && (this._Infos.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._Infos;
}
set
{
this._Infos.Assign(value);
}
}
Update:
This leads me to believe that I might need to find a way to modify the default value of HasLoadedOrAssignedValues when it is being deserialized from Newtons Json.Net.
I also found a dirty fix, which I don't really like that seems to be working:
var deserializedObject = JsonConvert.DeserializeObject<EasAlert>(json);
var list = new List<EasInfo>();
deserializedObject.EasInfos.SetSource(list); //<--- Fix, the Info will show up in json2
Assert.AreEqual(alertId, deserializedObject.AlertId);
Assert.AreEqual(infoId, deserializedObject.EasInfos[0].InfoId);

I was trying Microsoft's JavaScriptSerializer, and it worked fine. Code and output can be found below.
Could it be a bug in Newton's?
void Main()
{
var infoId = Guid.NewGuid();
var alertId = Guid.NewGuid();
var info = new Info();
info.InfoId = infoId;
var alert = new Alert();
alert.AlertId = alertId;
alert.Infos.Add(info);
var jss = new JavaScriptSerializer();
var json = jss.Serialize(alert); //JsonConvert.SerializeObject(alert);
Debug.WriteLine(json); //All looking good, nothing missing
var deserializedObject = jss.Deserialize<Alert>(json); //JsonConvert.DeserializeObject<Alert>(json);
(alertId == deserializedObject.AlertId).Dump(); //Assert is valid
(infoId == deserializedObject.Infos[0].InfoId).Dump(); //Assert is valid
var json2 = jss.Serialize(deserializedObject); //JsonConvert.SerializeObject(deserializedObject);
Debug.WriteLine(json2); //Infos is gone - NOT GONE!
}
public class Alert
{
public Guid AlertId { get; set; }
public List<Info> Infos { get; set; }
public Alert()
{
Infos = new List<Info>();
}
}
public class Info
{
public Guid InfoId { get; set; }
}
Output:
{"AlertId":"0340e855-065c-4ac7-868e-5999fa4b7862","Infos":[{"InfoId":"31e269a1-4354-423d-84bc-62f6dc06b10f"}]}
True
True
{"AlertId":"0340e855-065c-4ac7-868e-5999fa4b7862","Infos":[{"InfoId":"31e269a1-4354-423d-84bc-62f6dc06b10f"}]}

check Alert class (constructor)
public class Alert{
public Guid AlertId{get;set;}
public List<Info> Infos {get;set;}
public Alert() {
Infos = new List<Info>();
}
}
looks like you are missing List init
even with EntitySet

Related

Deserialize JSON to list with unknown object name in C#

I want to deserialize following JSON.
The problem is that the objects "ANDE" & "DAR" can change.
Means the objects are unknown and change depending on the JSON i wanna deserialize.
About 8000 different objects (ANDE, DAR, ...) need to be deserialized.
{"ANDE":
{"chart":[
{"date":"20180914","minute":"09:30"},{"date":"20180914","minute":"13:30"}]},
"DAR":
{"chart":[
{"date":"20180914","minute":"09:30"},{"date":"20180914","minute":"13:30"}]}}
I get the data by HTTP response and want to put into a List:
HttpResponseMessage response = client.GetAsync(API_PATH).GetAwaiter().GetResult();
List historicalDataList = response.Content.ReadAsAsync<List<HistoricalDataResponse>>().GetAwaiter().GetResult();
The HistoricalDataResponse class looks like:
public class HistoricalDataResponse
{
public string date { get; set; }
public string minute { get; set; }
}
How can i deserialize this kind of JSON with unknown objects in C#?
Any help is highly appreciated.
Then you should use a dynamic variable:
dynamic ReturnValue = JsonConvert.DeserializeObject(jsonstring);
note that as in dynamic objects, properties are determined after being assigned in runtime, so you will not get a drop down menu in design time, and also as its properties are unknown in design time, and property you test in design time even if its not correct, you wont get an error, and you will get the error in runtime when it is assigned.
dynamic ReturnValue = JsonConvert.DeserializeObject(jsonstring);
try
{
var a = ReturnValue.ANDE; // will work if it has ANDE property.
// do what you would do with ANDE
}
catch{}
try
{
var a = ReturnValue.DAR; // will work if it has DAR property.
// do what you would do with DAR
}
catch{}
Use a dictionary with string as key type :
void Main()
{
var client = new HttpClient();
HttpResponseMessage response = client.GetAsync("url").GetAwaiter().GetResult();
var json = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
var result = JsonConvert.DeserializeObject<Dictionary<string, DateResponse>>(json);
foreach (var element in result)
{
var key = element.Key; // ANDE
foreach (var item in element.Value.Chart)
{
var date = item.date;
var minute = item.minute;
}
}
}
public class DateResponse{
public List<HistoricalDataResponse> Chart { get; set; }
}
public class HistoricalDataResponse
{
public string date { get; set; }
public string minute { get; set; }
}

How can i see if my JSON contains a certain value and then compare it?

var Name = "Resources.myjson.json";
var NameJSON = new System.IO.StreamReader(typeof(Strings).GetTypeInfo().Assembly.GetManifestResourceStream(Name)).ReadToEnd();
var ParsedBrandJSON = Newtonsoft.Json.JsonConvert.DeserializeObject<TheInfo>(NameJSON);
await JsonCS.LoadJson(ParsedBrandJSON);
And on the page:
static public class TheInfoJSON
{
static public TheInfo Data { get; set; }
static public async Task LoadJson(Data JSON)
{
Data = JSON;
}
}
and
public class TheInfo
{
public List<TheName> TheName { get; set; } = new List<TheName>();
}
My json:
{
"TheInfo":[
{
"TheName": ["Martin", "Jonas", "Alex", "Oscar"]
}
]
}
When i now try to compare how can i see if my JSON contains a certain object and then store that as a single TheName? Is it possible to do it in the same cast?
var TheNameDataFromOtherPage = OtherPage.TheName; //Here i gather the name from another page that i will compare with the JSON
//Wrong syntax
bool DoTheyMatch = TheNameDataFromOtherPage == TheInfoJSON.Data.TheName.Contains("Alex");
This is now wrong syntax because i cant compare the value to a bool. How can i get out the data i find and then instead of having TheInfoJSON.Data.TheName.Contains("Alex"); as a bool, back to a single value of TheName containing "Alex" so I can create a bool out of the two values to see if the JSON has it or not.
I tried to add something along the lines like this after the contains(): as TheInfo.TheName but that isnt the correct syntax either.
bool DoTheyMatch = TheInfoJSON.Data.TheName.Contains(TheNameDataFromOtherPage);

Deserializing Json array with variable names first using C# Json.NET

I'm getting an irregular JSON array from the Census Bureau's public api.
The variable names are all in the first element, and I'm having trouble deserializing it.
http://api.census.gov/data/2014/pep/agesex?get=AGE,POP,SEX&for=us:*&DATE=7
gives me JSON like this:
[["AGE","POP","SEX","DATE","us"],
["0","3948350","0","7","1"],
["1","3962123","0","7","1"],
["2","3957772","0","7","1"],
["3","4005190","0","7","1"],
["4","4003448","0","7","1"],
["5","4004858","0","7","1"],
["6","4134352","0","7","1"],
["7","4154000","0","7","1"]]
I can successfully deserialize this using:
var test1 = JsonConvert.DeserializeObject<String[][]>(jsonStr);
However, I'm trying to deserialize it to a class like this:
public class TestClass
{
public string AGE { get; set; }
public string POP { get; set; }
public string SEX { get; set; }
public string DATE { get; set; }
public string us { get; set; }
}
I'm trying to do this:
var test2 = JsonConvert.DeserializeObject<TestClass[]>(jsonStr);
But I'm getting the following exception:
An exception of type 'Newtonsoft.Json.JsonSerializationException'
occurred in Newtonsoft.Json.dll but was not handled in user code
Additional information: Cannot create and populate list type
TestClass. Path '[0]', line 1, position
2.
There's two parts to this.
First is turning the JSON in to data usable in C#, and the second is turning that data in to nice objects.
Here's a working dotNetFiddle.net example of the following code: https://dotnetfiddle.net/Cr0aRL
Each row in your JSON is made up of an array of strings.
So that's an array of an array of strings.
In C# that can be written as string[][].
So to turn the JSON in to usable data with JSON.Net you can do:
var json = "[[\"AGE\",\"POP\",\"SEX\",\"DATE\",\"us\"],[\"0\",\"3948350\",\"0\",\"7\",\"1\"],[\"1\",\"3962123\",\"0\",\"7\",\"1\"],[\"2\",\"3957772\",\"0\",\"7\",\"1\"],[\"3\",\"4005190\",\"0\",\"7\",\"1\"],[\"4\",\"4003448\",\"0\",\"7\",\"1\"],[\"5\",\"4004858\",\"0\",\"7\",\"1\"],[\"6\",\"4134352\",\"0\",\"7\",\"1\"],[\"7\",\"4154000\",\"0\",\"7\",\"1\"]]";
var rawData = JsonConvert.DeserializeObject<string[][]>(json);
Next up is is turning that data in to objects.
The first row is the header, containing the column names, so we want to grab that, and then figure out the column index for each column name.
var headerRow = rawData.First();
var ageIndex = Array.IndexOf(headerRow, "AGE");
var popIndex = Array.IndexOf(headerRow, "POP");
var sexIndex = Array.IndexOf(headerRow, "SEX");
var dateIndex = Array.IndexOf(headerRow, "DATE");
var usIndex = Array.IndexOf(headerRow, "us");
Now we have the indexes, we need to take each row, and convert it in to the appropriate object. I've used LINQ for this as it's very good at representing data processing in a clear way.
var testData = rawData
.Skip(1) //The first row is a header, not data
.Select(dataRow => new TestClass()
{
AGE = dataRow[ageIndex],
POP = dataRow[popIndex],
SEX = dataRow[sexIndex],
DATE = dataRow[dateIndex],
us = dataRow[usIndex]
});
Finally a bit of testing, to make sure you have the data you're expecting.
//Get the second data row as an example
var example = testData.Skip(1).First();
//Output example POP to check value
Console.WriteLine(example.POP);
Everything above is very manual.
You have to know what headers you expect, then you manually find the indexes, then you manually map the rows to objects.
It's quite possible for a simple use case that doing that is fine. But in larger and/or more complex systems you might want/need to automate those steps.
Automating those steps is possible, but is beyond the scope of this answer as how you approach it can depend on a lot of different factors.
You could make a custom JsonConverter to handle this conversion during deserialization. The conversion code is really not much different than other answers here, except that it is encapsulated into a separate class so that you don't muddy up your main code with the conversion details. From the point of view of your main code it "just works".
Here is how to write the converter:
public class TestClassArrayConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(TestClass[]));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray table = JArray.Load(reader);
TestClass[] items = new TestClass[table.Count - 1];
for (int i = 1; i < table.Count; i++)
{
JArray row = (JArray)table[i];
items[i - 1] = new TestClass
{
AGE = (string)row[0],
POP = (string)row[1],
SEX = (string)row[2],
DATE = (string)row[3],
us = (string)row[4]
};
}
return items;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And here is how you would use it:
var test2 = JsonConvert.DeserializeObject<TestClass[]>(jsonStr, new TestClassArrayConverter());
Fiddle: https://dotnetfiddle.net/68Q0KT
You have to do the processing on your own, as there is no way the json deserializer can know, how to put the values into the respecitve variables.
If you know, this will be exactly this structure, you could for instance add an appropriate constructor
public TestClass(string[] values) {
AGE = values[0];
...
}
to your class. Then serialize your result to array of arrays of string and then pass the inner arrays to your constructor.
var t1 = JsonConvert.DeserializeObject<string[][]>(jsonStr);
//skip the first entry, as this contains the headers
var t2 = t1.Skip(1).Select(x=> new TestClass(x));
If your structure varies, you'll have to write some more complicated mapping code.
You will have to do some custom mapping as your Json does not have any naming conventions so you will have to work with the data in array and index formats. This will work:
var jsonStr = "[[\"AGE\",\"POP\",\"SEX\",\"DATE\",\"us\"], [\"0\",\"3948350\",\"0\",\"7\",\"1\"], [\"1\",\"3962123\",\"0\",\"7\",\"1\"], [\"2\",\"3957772\",\"0\",\"7\",\"1\"], [\"3\",\"4005190\",\"0\",\"7\",\"1\"], [\"4\",\"4003448\",\"0\",\"7\",\"1\"], [\"5\",\"4004858\",\"0\",\"7\",\"1\"], [\"6\",\"4134352\",\"0\",\"7\",\"1\"], [\"7\",\"4154000\",\"0\",\"7\",\"1\"]]";
var test2 = JsonConvert.DeserializeObject<string[][]>(jsonStr);
var test3 = test2.Select(x => new TestClass()
{
AGE = x[0].ToString(),
POP = x[1].ToString(),
SEX = x[2].ToString(),
DATE = x[3].ToString(),
us = x[4].ToString()
}).ToList();
//test Case
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
namespace ApiController.Test
{
[TestClass]
public class DownloadIrregularJsonStringObjects
{
string ApiKey => "YourPersonalCensusKey";
/// <summary>
/// You have to get your own ApiKey from the Census Website
/// </summary>
[TestMethod]
public void TestGetItem()
{
string url = $"http://api.census.gov/data/timeseries/healthins/sahie?get=NIC_PT,NAME,NUI_PT&for=county:*&in=state:*&time=2015&key={YourPersonalCensusKey}";
string expected = "Autauga County, AL";
IList<HealthData> actual = ApiController.DownloadIrregularJsonStringObjects.GetCensusHealthData(url);
Assert.AreEqual(actual[0].NAME, expected);
}
}
}
///Actual Assembly
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
namespace ApiController
{
public class DownloadIrregularJsonStringObjects
{
public static IList<HealthData> GetCensusHealthData(string url)
{
var json = GetData(url);
var rawData = JsonConvert.DeserializeObject<string[][]>(json);
var headerRow = rawData.First();
var nic_pt_Index = Array.IndexOf(headerRow, "NIC_PT");
var name_Index = Array.IndexOf(headerRow, "NAME");
var nui_pt_Index = Array.IndexOf(headerRow, "NUI_PT");
IList<HealthData> retVal = new List<HealthData>();
foreach (var r in rawData.Skip(1))
{
HealthData dataRow = new HealthData();
dataRow.NIC_PT = r[nic_pt_Index];
dataRow.NAME = r[name_Index];
dataRow.NUI_PT = r[nui_pt_Index];
retVal.Add(dataRow);
}
return retVal;
}
private static string GetData(string url)
{
using (var w = new WebClient())
{
var jsonData = string.Empty;
jsonData = w.DownloadString(url);
return jsonData;
}
}
}
public class HealthData
{
public string NIC_PT { get; set; }
public string NAME { get; set; }
public string NUI_PT { get; set; }
}
}

Reading POST Request XML - Boolean value always read as false

I'm working on a WCF RESTful web service hosted in IIS. I'm currently working on a fairly simple post request, sending the following XML to the endpoint:
<StockListRequestData xmlns="http://myWebService.com/endpoint">
<UserID>2750</UserID>
<StockDatabase>stockLeekRoadVenue</StockDatabase>
<InStockOnly>true</InStockOnly>
</StockListRequestData>
This XML matches a DataContract on my web service:
[DataContract(Namespace = "http://myWebService.com/endpoint")]
public class StockListRequestData
{
[DataMember]
public string UserID { get; set; }
[DataMember]
public string StockDatabase { get; set; }
[DataMember]
public bool InStockOnly { get; set; }
}
The problem is the <InStockOnly>true</InStockOnly> element, when I set this to true or false it will always be interpreted as false...
Here is the code that handles the request:
public StockListResponseData GetListOfProducts(StockListRequestData requestData)
{
var stockList = new StockList(requestData.InStockOnly, requestData.StockDatabase);
StockListResponseData response;
if (stockList.Any())
{
var stockArray = new Stock[stockList.Count];
var i = 0;
foreach (var s in stockList)
{
stockArray[i] = s;
i++;
}
response = new StockListResponseData
{
StockList = stockArray,
WasSuccessful = true,
};
return response;
}
response = new StockListResponseData
{
WasSuccessful = false
};
return response;
}
The StockList class:
[DataContract]
public class StockList : List<Stock>
{
public StockList(bool inStockOnly, string stockDb)
{
if (inStockOnly)
{
// Get only products that are in stock
var conn = AndyServerDatabase.ConnectToStockMovementByDb(stockDb);
conn.Open();
// Compile SQL query
var q = new SqlCommand(null, conn) { CommandText = "SELECT StockID, Name, PerBox FROM Stock WHERE InStock = 1;" };
// Execute query
var rdr = q.ExecuteReader();
// Check that the output isn't null
if (rdr.HasRows)
{
while(rdr.Read())
{
var id = Convert.ToInt32(rdr[0]);
var name = rdr[1].ToString();
var perBox = Convert.ToInt32(rdr[2]);
Add(new Stock(id, name, perBox));
}
conn.Close();
}
// Output is null
conn.Close();
}
else
{
// Get all products
// Get only products that are in stock
var conn = AndyServerDatabase.ConnectToStockMovementByDb(stockDb);
conn.Open();
// Compile SQL query
var q = new SqlCommand(null, conn) { CommandText = "SELECT StockID, Name, PerBox FROM Stock;" };
q.Prepare();
// Execute query
var rdr = q.ExecuteReader();
// Check that the output isn't null
if (rdr.HasRows)
{
while (rdr.Read())
{
var id = Convert.ToInt32(rdr[0]);
var name = rdr[1].ToString();
var perBox = Convert.ToInt32(rdr[2]);
Add(new Stock(id, name, perBox));
}
conn.Close();
}
// Output is null
conn.Close();
}
// Add();
}
}
Resultant XML:
<StockListResponseData xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<StockList xmlns:a="http://schemas.datacontract.org/2004/07/SMS">
<a:Stock>
<a:Id>1</a:Id>
<a:Name>Smirnoff Vodka (70cl)</a:Name>
<a:PerBox>6</a:PerBox>
<a:Quantity>0</a:Quantity>
<a:Remains>0</a:Remains>
</a:Stock>
<a:Stock>
<a:Id>2</a:Id>
<a:Name>Jagermeister (70cl)</a:Name>
<a:PerBox>6</a:PerBox>
<a:Quantity>0</a:Quantity>
<a:Remains>0</a:Remains>
</a:Stock>
</StockList>
<WasSuccessful>true</WasSuccessful>
I hope this is enough to go on, I've been stumped for ages and just can't figure out why it's behaving in such a way.. if you need additional code I haven't included please feel free to ask.
Many thanks,
Andy
Edit:
To add some context to show what is happening:
For example, I know that:
<a:Stock>
<a:Id>2</a:Id>
<a:Name>Jagermeister (70cl)</a:Name>
<a:PerBox>6</a:PerBox>
<a:Quantity>0</a:Quantity>
<a:Remains>0</a:Remains>
</a:Stock>
Has its InStock row set to 0, which means that this should not be displayed in the resultant XML if I pass in true.
I have changed the StockList constructors if(inStockOnly) to if(!inStockOnly) - I then pass in <InStockOnly>true</InStockOnly> - when it gets to the StockList constructor it is then inverted and the correct data is displayed - so it must be reading it as false by the time it gets to this if statement.
If I pass in <InStockOnly>false</InStockOnly> it still displays the "correct" results, therefore it is reading it as false until it gets to the inversion!
Likewise if I leave it as if(inStockOnly) and pass in <InStockOnly>false</InStockOnly> it displays the data for false!
I have also added requestData.InStockOnly to the StockListResponseData DataContract and there it displays it outputs the value of requestData.InStockOnly as false.
Your discovery has led me to the explanation, and someone with a problem similar to yours:
WCF DataContract DataMember order?
http://msdn.microsoft.com/en-us/library/ms729813.aspx
Next in order are the current type’s data members that do not have the Order property of the DataMemberAttribute attribute set, in alphabetical order.
When the order of data members is not explicitly specified, their serialization order is alphabetical. That explains why InStockOnly worked when it was moved to the top, because it's first alphabetically. On the other hand, why StockDatabase worked is a bit of a mystery, because that's after UserId alphabetically (does AndyServerDatabase.ConnectToStockMovementByDb() use a default value if StockDb is null?).
For the sake of argument, if for whatever reason you wanted to keep the order you have there, you could do this:
[DataContract(Namespace = "http://myWebService.com/endpoint")]
public class StockListRequestData
{
[DataMember(Order = 0)]
public string UserID { get; set; }
[DataMember(Order = 1)]
public string StockDatabase { get; set; }
[DataMember(Order = 2)]
public bool InStockOnly { get; set; }
}
In fact, it's probably a good practice to explicitly indicate the order, so adding new properties later doesn't break anything.
I tried the suggestion above and it still didn't work! Kept searching and found another solution, actually setting the 'Specified' property to true:
PackageImagesPayload payload = new PackageImagesPayload();
payload.UsesSplitBy = usesSplitBy;
payload.UsesSplitBySpecified = true;

Ignore custom children with Json .Net

i have a json response like this:
{"response_values":[110,{"id":14753,"name":"piter"},{"id":14753,"name":"rabbit"}]}
and i have a simple class
public class Class1
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
and when i trying to convert json to list of the objects with this method:
public T Cast<T>(string json)
{
var result = default(T);
var jsonObject = JObject.Parse(json);
if (jsonObject != null)
{
var responseToken = jsonObject["response"];
result = responseToken.ToObject<T>();
}
return result;
}
like this
...
var jsonString = GetJson();
var items = Cast<List<Class1>>();
...
i have an exceiption, because value "110" is integer. How can i skip this value?
You always have this option if you expect the values to ignore to always be at the beginning:
if (jsonObject != null)
{
var responseToken = parsed["response_values"].SkipWhile(j => j.Type != JTokenType.Object);
if (responseToken.Count() > 0) result = responseToken.ToObject<T>();
}
You may prefer to use Skip(1) instead of SkipWhile if it's always the first value. Alternatively, you can use Where to ignore or select tokens anywhere in the message.
Of course, you can play around with this approach (changing things) depending on exactly what you expect to be returned in success scenarios.

Categories