问题
StackOverflow Community!
I have a chatbot, and integrated LUIS.ai to make it smarter. One of the dialogues is about to Book an appointment with a Supervisor (Teacher) Everything has been working fine, with literally the same code. A couple of hours ago I am experiencing some strange errors.
Exception: Type 'Newtonsoft.Json.Linq.JArray' in Assembly 'Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' is not marked as serializable.
How to reproduce the error?
If the both Entity (Teacher and Date) is missing from the user's input it works FINE, the bot builds the Form, asking for missing inputs and presents the suggested meeting times.
If one of the Entity is missing it from input it will build a Form and asks for the missing Date or Teacher Entity and presents the suggested meeting times.
BUT
If the user's input contains both Entity: the Teacher and the Date too, then I am getting the error.
Here is my WebApiConfig class:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Json settings
config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented;
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Formatting = Newtonsoft.Json.Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
};
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
I am only experiencing this error when I am trying to get an Entity from the user utterance, which is a type of builtin.dateTimeV2.
This async method is called:
//From the LUIS.AI language model the entities
private const string EntityMeetingDate = "MeetingDate";
private const string EntityTeacher = "Teacher";
[LuisIntent("BookSupervision")]
public async Task BookAppointment(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
{
var message = await activity;
await context.PostAsync($"I am analysing your message: '{message.Text}'...");
var meetingsQuery = new MeetingsQuery();
EntityRecommendation teacherEntityRecommendation;
EntityRecommendation dateEntityRecommendation;
if (result.TryFindEntity(EntityTeacher, out teacherEntityRecommendation))
{
teacherEntityRecommendation.Type = "Name";
}
if (result.TryFindEntity(EntityMeetingDate, out dateEntityRecommendation))
{
dateEntityRecommendation.Type = "Date";
}
var meetingsFormDialog = new FormDialog<MeetingsQuery>(meetingsQuery, this.BuildMeetingsForm, FormOptions.PromptInStart, result.Entities);
context.Call(meetingsFormDialog, this.ResumeAfterMeetingsFormDialog);
}
Further methods for building a form:
private IForm<MeetingsQuery> BuildMeetingsForm()
{
OnCompletionAsyncDelegate<MeetingsQuery> processMeetingsSearch = async (context, state) =>
{
var message = "Searching for supervision slots";
if (!string.IsNullOrEmpty(state.Date))
{
message += $" at {state.Date}...";
}
else if (!string.IsNullOrEmpty(state.Name))
{
message += $" with professor {state.Name}...";
}
await context.PostAsync(message);
};
return new FormBuilder<MeetingsQuery>()
.Field(nameof(MeetingsQuery.Date), (state) => string.IsNullOrEmpty(state.Date))
.Field(nameof(MeetingsQuery.Name), (state) => string.IsNullOrEmpty(state.Name))
.OnCompletion(processMeetingsSearch)
.Build();
}
private async Task ResumeAfterMeetingsFormDialog(IDialogContext context, IAwaitable<MeetingsQuery> result)
{
try
{
var searchQuery = await result;
var meetings = await this.GetMeetingsAsync(searchQuery);
await context.PostAsync($"I found {meetings.Count()} available slots:");
var resultMessage = context.MakeMessage();
resultMessage.AttachmentLayout = AttachmentLayoutTypes.Carousel;
resultMessage.Attachments = new List<Attachment>();
foreach (var meeting in meetings)
{
HeroCard heroCard = new HeroCard()
{
Title = meeting.Teacher,
Subtitle = meeting.Location,
Text = meeting.DateTime,
Images = new List<CardImage>()
{
new CardImage() {Url = meeting.Image}
},
Buttons = new List<CardAction>()
{
new CardAction()
{
Title = "Book Appointment",
Type = ActionTypes.OpenUrl,
Value = $"https://www.bing.com/search?q=easj+roskilde+" + HttpUtility.UrlEncode(meeting.Location)
}
}
};
resultMessage.Attachments.Add(heroCard.ToAttachment());
}
await context.PostAsync(resultMessage);
}
catch (FormCanceledException ex)
{
string reply;
if (ex.InnerException == null)
{
reply = "You have canceled the operation.";
}
else
{
reply = $"Oops! Something went wrong :( Technical Details: {ex.InnerException.Message}";
}
await context.PostAsync(reply);
}
finally
{
context.Wait(DeconstructionOfDialog);
}
}
private async Task<IEnumerable<Meeting>> GetMeetingsAsync(MeetingsQuery searchQuery)
{
var meetings = new List<Meeting>();
//some random result manually for demo purposes
for (int i = 1; i <= 5; i++)
{
var random = new Random(i);
Meeting meeting = new Meeting()
{
DateTime = $" Available time: {searchQuery.Date} At building {i}",
Teacher = $" Professor {searchQuery.Name}",
Location = $" Elisagårdsvej 3, Room {random.Next(1, 300)}",
Image = $"https://placeholdit.imgix.net/~text?txtsize=35&txt=Supervision+{i}&w=500&h=260"
};
meetings.Add(meeting);
}
return meetings;
}
The strangest thing that this code has worked, and my shoutout and respect goes for the community on GitHub, because I think this is an awesome platform with a tons of documentation and samples.
回答1:
This is known issue (also reported here and here).
Long story short, since the builtin.datetimeV2.* entities are not yet supported in BotBuilder, the Resolution
dictionary of the EntityRecommendation
ends up with an entry with a value of type JArray
. The problem arises when you pass those entities to a FormDialog
. Since the entities are a private field in the dialog and of course, as any other dialog, is being serialized, an exception is being thrown because the JArray
class from Newtonsoft
is not marked as serializable.
The request for adding support for datetimeV2 entities is here.
The workaround I can think of right now is to extract the value of the DateTime entity manually and assign it your Date
field of your MeetingsQuery
instance you are passing to the FormDialog
and also to remove the DateTime entity from the result.Entities
collection that you are passing to the FormDialog
.
Update
This is already fixed in the SDK as you can see in this Pull Request.
来源:https://stackoverflow.com/questions/44552030/json-serializer-error-with-botframework-an-luis