I would like to read a dynamic object from a json file and then use this in a stringTemplate.
The following code works.
dynamic data = new { bcName = "Lixam B.V", periodName = "July 2013" };
var engine = new Template("<m.bcName> <m.periodName>");
engine.Add("m", data);
engine.Render().Should().Be("Lixam B.V July 2013");
The following code fails
var json = "{bcName : 'Lixam B.V', periodName : 'July 2013'}";
dynamic data = JsonConvert.DeserializeObject(json);
string name = (data.bcName);
name.Should().Be("Lixam B.V"); // this passes
var engine = new Template("<m.bcName> <m.periodName>");
engine.Add("m", data);
engine.Render().Should().Be("Lixam B.V July 2013"); //fails
Is there another way to configure JsonConverter to be compatible with StringTemplate
You need to create an IModelAdaptor for whatever the compiled type representing dynamic is, and register it using TemplateGroup.RegisterModelAdaptor.
Inspired on Mr. Harwell's answer, I've implemented an IModelAdaptor that enable the usage of Newtonsoft.Json parsed objects.
Here it goes:
internal class JTokenModelAdaptor : Antlr4.StringTemplate.IModelAdaptor
{
public object GetProperty(
Antlr4.StringTemplate.Interpreter interpreter,
Antlr4.StringTemplate.TemplateFrame frame,
object obj,
object property,
string propertyName)
{
var token = (obj as JToken)?.SelectToken(propertyName);
if (token == null)
return null;
if (token is JValue)
{
var jval = token as JValue;
return jval.Value;
}
return token;
}
}
You just need to register the adaptor in your template group, like this:
template.Group.RegisterModelAdaptor(typeof(JToken), new JTokenModelAdaptor());
Related
We want to get the UserName from the ServiceStack session, but we find that the backslashes in the UserName are not deserialized as expected. The UserName has this format 'domainname\username' and serialized in a jwt token this looks like:
{
"typ": "JWT",
"alg": "HS256"
}.{
"iss": "ssjwt",
"iat": 1635952233,
"exp": 1635955833,
"name": "Robin Doe",
"preferred_username": "domainname\\robindoe"
}.[Signature]
After calling:
var sessionFromJwt = JwtAuthProviderReader.CreateSessionFromJwt(req);
userName = sessionFromJwt.UserName;
The userName variable contains the value 'domainname\\robindoe' instead of 'domainname\robindoe'.
After digging in the ServiceStack code, we pin this down to the PopulateFromMap() method in https://github.com/ServiceStack/ServiceStack/blob/36df74a8b1ba7bf06f85262c1155e1425c082906/src/ServiceStack/Auth/UserAuth.cs#L388.
To demonstrate this problem we have written a small program to prove the point:
class Program
{
static void Main(string[] args)
{
var jwtPayload = JsonObject.Parse(#"{
""iss"": ""ssjwt"",
""iat"": 1635952233,
""exp"": 1635955833,
""name"": ""John Doe"",
""preferred_username"": ""domainname\\username""
}");
var session = new AuthUserSession();
// The PopulateFromMap implementation does not deserialize the json values according to json standards
UserAuthExtensions.PopulateFromMap(session, jwtPayload);
// Notice that the session.UserName still has the escape character 'domainname\\username' instead of the expected 'domainname\username'
Console.WriteLine(session.UserName);
// The PopulateFromMap should deserialize also the values, like in test Can_dynamically_parse_JSON_with_escape_chars()
Can_dynamically_parse_JSON_with_escape_chars();
}
private const string JsonCentroid = #"{""place"":{ ""woeid"":12345, ""placeTypeName"":""St\\a\/te"" } }";
// Source: https://github.com/ServiceStack/ServiceStack.Text/blob/master/tests/ServiceStack.Text.Tests/JsonObjectTests.cs
public static void Can_dynamically_parse_JSON_with_escape_chars()
{
var placeTypeName = JsonObject.Parse(JsonCentroid).Object("place").Get("placeTypeName");
if (placeTypeName != "St\\a/te")
throw new InvalidCastException(placeTypeName + " != St\\a/te");
placeTypeName = JsonObject.Parse(JsonCentroid).Object("place").Get<string>("placeTypeName");
if (placeTypeName != "St\\a/te")
throw new InvalidCastException(placeTypeName + " != St\\a/te");
}
}
Why does UserAuthExtensions.PopulateFromMap(session, jwtPayload) does not deserialize json values with escape correctly in ServiceStack.Auth?
The issue is due to enumerating a JsonObject didn't return the same escaped string value as indexing it which has been resolved from this commit.
This change is available from v5.12.1+ that's now available on MyGet.
After saving DateTime in controller, I pass it back to the View but when it's displayed in view the value became /Date(1545062400000)/.
I already checked in the controller while in process if the data was changed but it did not since I'm just passing the viewmodel in the view.
[HttpPost]
public ActionResult UpdateHeader(RecordViewModel recordViewModel)
{
var ResultMessage = "";
if (ModelState.IsValid)
{
Record record = (from c in context.Records
where c.RecordId == recordViewModel.RecordId
select c).FirstOrDefault();
if (record == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
record.Description = recordViewModel.Description;
record.Date = recordViewModel.Date;
record.Remarks = recordViewModel.Remarks;
try
{
context.SaveChanges();
ResultMessage = "Record successfully updated.";
}
catch (Exception ex)
{
ModelState.AddModelError("", ErrorHelper.GetInnerException(ex));
ResultMessage = "Error: " + ErrorHelper.GetInnerException(ex);
}
}
var result = new { Model = recordViewModel, ResultMessage = ResultMessage };
return Json(result, JsonRequestBehavior.AllowGet);
}
Angular
self.submitEdit = function () {
var updateRecordHeader = RecordServices.updateRecordHeader(self.header)
.then(function successCallback(response) {
self.header = response.data.Model;
self.header.ResultMessage = response.data.ResultMessage;
}, function errorCallback(response) { toastr.error(response.statusText); });
}
The /Date(1545062400000)/ is known as date ticks using UNIX timestamp format since RFC7519 "epoch" (January 01, 1970), which cannot be directly consumed as JS Date object without converting it first. The reason behind usage of the ticks is JSON format doesn't have specific representation for DateTime struct when serialized to plain strings (see this reference).
You can create a custom function to convert ticks into JS Date object:
function toJSDate(value) {
var regex = /Date\(([^)]+)\)/;
var results = regex.exec(value);
var date = new Date(parseFloat(results[1])); // or parseInt
return date;
}
Or even simpler without regex by picking numeric values directly like this:
function toJSDate(value) {
var date = new Date(parseFloat(value.substring(6))); // or parseInt
return date;
}
Then use that function for ticks-to-date conversion:
// example
var header = response.data.Model;
var dateObject = toJSDate(header.Date);
// assign date object afterwards
Note that you may need to create another object structure which resembles response.data.Model but using server-side Date property with JS date object.
As an alternative you may create a getter-only string property which uses ToString() to convert DateTime value into desired string representation, then use it inside JS.
Side note:
Avoid using viewmodel property name which exactly matches built-in JS function names & objects (i.e. Date) for clarity.
Related issues:
How do I format a Microsoft JSON date?
Converting .NET DateTime to JSON
I'm trying out things with Flutter right now. But my variables keep getting reinitialised when accessed from another class.
I'm using json parsing and i need two parts of my request. The "Relatorio" part and the "Mensagem" part.
to parse this json i'm doing this:
List<RelatorioProdutos> parseRelatorioPorProduto(String responseBody) {
final parsed = json.decode(responseBody);
var relatorio = parsed['Relatorio'];
var mensagem = parsed['Mensagem'];
print (mensagem); // Here the variable returns well,
//but when i need it in other class i receive null.
return relatorio.map<RelatorioProdutos>((json) => new RelatorioProdutos.fromJson(json)).toList();
}
class RelatorioProdutos {
String CodigoProduto;
var QtdVendida;
var TotalVendas;
String Descricao;
RelatorioProdutos({this.CodigoProduto, this.QtdVendida, this.TotalVendas, this.Descricao,});
factory RelatorioProdutos.fromJson(Map json) {
//returns a List of Maps
return new RelatorioProdutos(
CodigoProduto: json['CodigoProduto'] as String,
QtdVendida: json['QtdVendida'],
TotalVendas: json['TotalVendas'],
Descricao: json['Descricao'] as String,
);
}
}
I want to use this 'mensagem' variable in another class to show the error for user, but i always receive 'null'.
i already tried setState but it reloads my json and i dont want to request the RestServer again.
Thanks from now!
If I understand correctly, you want to access a local variable of a function from another class. I don't think it's possible.
One way to do it, would be to wrap your response in another object containing the response, and this variable:
List<Response<RelatorioProdutos>> parseRelatorioPorProduto(
String responseBody) {
final parsed = json.decode(responseBody);
var relatorio = parsed['Relatorio'];
var mensagem = parsed['Mensagem'];
print(mensagem); // Here the variable returns well,
//but when i need it in other class i receive null.
return relatorio
.map((json) => new Response<RelatorioProdutos>(
new RelatorioProdutos.fromJson(json), mensagem))
.toList();
}
class RelatorioProdutos {
String CodigoProduto;
var QtdVendida;
var TotalVendas;
String Descricao;
RelatorioProdutos({
this.CodigoProduto,
this.QtdVendida,
this.TotalVendas,
this.Descricao,
});
factory RelatorioProdutos.fromJson(Map json) {
//returns a List of Maps
return new RelatorioProdutos(
CodigoProduto: json['CodigoProduto'] as String,
QtdVendida: json['QtdVendida'],
TotalVendas: json['TotalVendas'],
Descricao: json['Descricao'] as String,
);
}
}
class Response<T> {
const Response(
this.value,
this.errorMessage,
);
final T value;
final String errorMessage;
bool get hasError => errorMessage != null;
}
In this example I created a Response object that can contains both the response value and an error message.
In the parseRelatorioPorProduto, instead of returning the relatorio, I changed the return type to Response<RelatorioProdutos> in order to have access to the value and the error message from any class which call this function.
Thanks Letsar, i tried yout ideia but i get a lot of others erros.
To solve this problem i used this:
List<RelatorioProdutos> parseRelatorioPorProduto(String responseBody) {
final parsed = json.decode(responseBody);
var relatorio = parsed['Relatorio'];
var mensagem = parsed['Mensagem'];
if(mensagem[0].toString().substring(16,17) == "0"){
List<RelatorioProdutos> asd = new List();
RelatorioProdutos aimeudeus = new RelatorioProdutos(Descricao: mensagem[0].toString(), CodigoProduto: "a", TotalVendas: 0, QtdVendida: 0);
asd.add(aimeudeus);
return asd;
}else{
return relatorio.map<RelatorioProdutos>((json) => new RelatorioProdutos.fromJson(json)).toList();
}
}
I have a file that contains a json array of objects:
[
{
"test1": "abc"
},
{
"test2": [1, 2, 3]
}
]
I wish to use use Jackson's JsonParser to take an inputstream from this file, and at every call to .next(), I want it to return an object from the array until it runs out of objects or fails.
Is this possible?
Use case:
I have a large file with a json array filled with a large number of objects with varying schemas. I want to get one object at a time to avoid loading everything into memory.
EDIT:
I completely forgot to mention. My input is a string that is added to over time. It slowly accumulates json over time. I was hoping to be able to parse it object by object removing the parsed object from the string.
But I suppose that doesn't matter! I can do this manually so long as the jsonParser will return the index into the string.
Yes, you can achieve this sort of part-streaming-part-tree-model processing style using an ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
JsonParser parser = mapper.getFactory().createParser(new File(...));
if(parser.nextToken() != JsonToken.START_ARRAY) {
throw new IllegalStateException("Expected an array");
}
while(parser.nextToken() == JsonToken.START_OBJECT) {
// read everything from this START_OBJECT to the matching END_OBJECT
// and return it as a tree model ObjectNode
ObjectNode node = mapper.readTree(parser);
// do whatever you need to do with this object
}
parser.close();
What you are looking for is called Jackson Streaming API. Here is a code snippet using Jackson Streaming API that could help you to achieve what you need.
JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createJsonParser(new File(yourPathToFile));
JsonToken token = parser.nextToken();
if (token == null) {
// return or throw exception
}
// the first token is supposed to be the start of array '['
if (!JsonToken.START_ARRAY.equals(token)) {
// return or throw exception
}
// iterate through the content of the array
while (true) {
token = parser.nextToken();
if (!JsonToken.START_OBJECT.equals(token)) {
break;
}
if (token == null) {
break;
}
// parse your objects by means of parser.getXxxValue() and/or other parser's methods
}
This example reads custom objects directly from a stream:
source is a java.io.File
ObjectMapper mapper = new ObjectMapper();
JsonParser parser = mapper.getFactory().createParser( source );
if ( parser.nextToken() != JsonToken.START_ARRAY ) {
throw new Exception( "no array" );
}
while ( parser.nextToken() == JsonToken.START_OBJECT ) {
CustomObj custom = mapper.readValue( parser, CustomObj.class );
System.out.println( "" + custom );
}
This is a late answer that builds on Ian Roberts' answer. You can also use a JsonPointer to find the start position if it is nested into a document. This avoids custom coding the slightly cumbersome streaming token approach to get to the start point. In this case, the basePath is "/", but it can be any path that JsonPointer understands.
Path sourceFile = Paths.get("/path/to/my/file.json");
// Point the basePath to a starting point in the file
JsonPointer basePath = JsonPointer.compile("/");
ObjectMapper mapper = new ObjectMapper();
try (InputStream inputSource = Files.newInputStream(sourceFile);
JsonParser baseParser = mapper.getFactory().createParser(inputSource);
JsonParser filteredParser = new FilteringParserDelegate(baseParser,
new JsonPointerBasedFilter(basePath), false, false);) {
// Call nextToken once to initialize the filteredParser
JsonToken basePathToken = filteredParser.nextToken();
if (basePathToken != JsonToken.START_ARRAY) {
throw new IllegalStateException("Base path did not point to an array: found "
+ basePathToken);
}
while (filteredParser.nextToken() == JsonToken.START_OBJECT) {
// Parse each object inside of the array into a separate tree model
// to keep a fixed memory footprint when parsing files
// larger than the available memory
JsonNode nextNode = mapper.readTree(filteredParser);
// Consume/process the node for example:
JsonPointer fieldRelativePath = JsonPointer.compile("/test1");
JsonNode valueNode = nextNode.at(fieldRelativePath);
if (!valueNode.isValueNode()) {
throw new IllegalStateException("Did not find value at "
+ fieldRelativePath.toString()
+ " after setting base to " + basePath.toString());
}
System.out.println(valueNode.asText());
}
}
I have create a one sample WCF rest template WebApi in this i have use Entity Framework to getting the data when i run the service for it return the string value it showing the result but at end of the json value add XML code like below how can i solve this.
[{"AccountId":1,
"AccountNumber":"AC001",
"AccountType":"Restaurant",
"BusinessName":"Red SpiceInc",
"PrimaryContactFirstName":"Varma",
"PrimaryContactLastName":"Bhupatiraju",
"PrimaryContactPhone":"(949) 374 2114",
"PrimaryContactEmail":"redspice#mybusinessapp.com",
"AccountGuid":"918D3E66-CEFE-11E0-8C2F-0C0B4824019B",
"EntityState":1,"EntityKey":null}]
<?xml version="1.0" encoding="utf-8"?><Stream p1:nil="true" xmlns:p1="w3.org/2001/XMLSchema-instance"; />
My code
[WebGet(UriTemplate = "GetSetting({LocationGuid},{settingName})", ResponseFormat = WebMessageFormat.Json)]
public Stream GetSetting(string LocationGuid, string settingName)
{
string str = string.Empty;
string strJSON = string.Empty;
dynamic contactResponse = new JsonObject();
List<setting> Result;
Result = new List<setting>();
var Location = from acc in objEntity.locations where acc.LocationGuid == LocationGuid select acc;
if (Location.Count() > 0)
{
var LocationId = (from acc in objEntity.locations where acc.LocationGuid == LocationGuid select acc).First();
var objSetting = from cat in objEntity.settings where cat.SettingName == settingName & cat.LocationId == LocationId.LocationId select cat;
setting SettingList = new setting();
foreach (setting setting in objSetting)
{
setting Settinglist = new setting();
Settinglist.SettingId = setting.SettingId;
Settinglist.AccountId = setting.AccountId;
Settinglist.LocationId = setting.LocationId;
Settinglist.SettingName = setting.SettingName;
Settinglist.SettingValue = setting.SettingValue;
Settinglist.FieldType = setting.FieldType;
Result.Add(Settinglist);
}
JavaScriptSerializer js = new JavaScriptSerializer();
strJSON = js.Serialize(Result);
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json; charset=utf-8";
return new MemoryStream(Encoding.UTF8.GetBytes(strJSON));
}
else
{
return null;
}
}
Please help me solve this problem.
I believe return the POCO is enough
the method signature is
public List<setting> GetSetting(string, string)
WCF Web API will serialize the object into json or xml for you as per your request header (accept: application/json, or application/xml)
As was mentioned in misaxi's response, the WebApi WebGet operations generally do not need to concern themselves with how the response is returned. The responsibility of the web operation is to simply return the data. E.g....
[WebGet(UriTemplate = "GetSetting({LocationGuid},{settingName})"]
public List<setting> GetSetting(string LocationGuid, string settingName)
{
List<setting> Result = null;
var Location = from acc in objEntity.locations where acc.LocationGuid == LocationGuid select acc;
if (Location.Count() > 0)
{
Result = new List<setting>();
....
foreach (setting setting in objSetting)
{
....
Result.Add(Settinglist);
}
}
return Result;
}
The fact that the client receives XML, JSON, JSONP, HTML, etc is up to the client (in conjunction with server-side support). The client's request header will include something that looks like Accept: application/json or Accept: application/xml or whatever representation that the client is after. WebApi comes pre-loaded with a few standard formats, XML (the default) and Json. You can introduce more formats as needed to accept a more diverse set of Accept: .... headers, but the conversion from your data to these requested formats are generally out-of-scope for your web operation and rely on the formatters you set up (or were set up by default) when your service is initialized.
Likewise, WebPost operations generally do not care if the body of the request was XML or JSON, by the time the operation is invoked, formatters have already translated that payload into your method's parameters.