I have a custom template ~/Views/Shared/EditorTemplate/String.cshtml and it seems to be causing the Exception:
The model item passed into the dictionary is of type 'Proj.Models.EnumType', but this dictionary requires a model item of type 'System.String'.
It seems to only happen to Enums. It also goes away if I remove the template. The template doesn't seem to cause it, I don't think it's even making it that far. I can put ANYTHING in there and the exception is the same.
So... can I not use an #Html.EditorFor with a model with an enum if I have a custom template?
Some context:
Model:
namespace Proj.Models
{
public enum EnumType
{
A = 0,
B = 1,
C = 2,
}
public class Mod
{
[Required]
public String Name;
[Required]
public EnumType Letter;
}
}
View:
#model Proj.Models.Mod
#Html.EditorFor(m=>m) // Exception happens here
Here is what I found to work for me.
In your template, make sure you declare your model as nullable enum type. Then, in code, check to see if it has a value and based on that do appropriate formatting.
#inherits System.Web.Mvc.WebViewPage<Proj.Models.EnumType?>
#{
Proj.Models.EnumType v = Model.HasValue ? Model.Value : {*some-default-value*};
#Html.{*Your-formatter-here*};
}
Related
I use Spatie's enums in my symfony project and I made a custom DBAL type for those objects. When I save an enum object to the database, I save it in a special string format. The conversion function in my EnumType looks like this:
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if ($value === null) {
return null;
}
return get_class($value) . '::' . $value->getIndex() . '::' . $value->getValue();
}
So for example I have a transaction status enum that looks like this:
namespace App\Enum;
use Spatie\Enum\Enum;
/**
* #method static self failed()
* #method static self pending()
* #method static self completed()
*/
final class TransactionStatus extends Enum {}
And when I save it to the database it can turn into any one of these strings respectively:
App\Enum\TransactionStatus::0::failed
App\Enum\TransactionStatus::1::pending
App\Enum\TransactionStatus::2::completed
This helps my EnumType to know what enum to transform it back into. And the reason I use the index number in the string is because that helps with sorting.
Now this all works very well for fetching and saving my entities to the database. But when I try to use an enum in a where clause of a DQL statement, it doesn't work at all.
namespace App\Repository;
use App\Entity\Transaction;
use App\Enum\TransactionStatus;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
class TransactionRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry)
{
parent::__construct($registry, Transaction::class);
}
public function findByStatus(TransactionStatus $status)
{
return $this->createQueryBuilder('t')
->andWhere('t.status = :status')
->setParameter('status', $status)
->getQuery()->getResult();
}
}
Because for some reason doctrine ignores my conversion function and just uses the __toString() function that's built into Spatie's enum. So doctrine is looking for the string "pending" instead of "App\Enum\TransactionStatus::1::pending".
How do I make sure that my enums always get converted correctly in a DQL where clause?
Okay I found a way to do it, even though it is super hacky. I just check from where the __toString() method is called and if it's called from Doctrine's DBAL then I use the DB format of the enum.
namespace App\Enum;
use Spatie\Enum\Enum as BaseEnum;
abstract class Enum extends BaseEnum
{
public function __toString(): string
{
if (debug_backtrace()[1]['class'] === 'PDOStatement') {
return get_class($this) . "::$this->index::$this->value";
}
return parent::__toString();
}
}
Looking to create an enum for some regex to store in my application to allow for code reuse.
Eg:
export enum Regex {
ONE_DANK_REGEX = /^[dank]+$/g,
FIVE_OUT_OF_SEVEN = /^[meme]{5,7}$/g
}
But:
By not using string literals, I get the TS90010 error: Type RegExpis not assignable to type Regex.
By using string literals, I can't use these in a pattern attribute on my inputs
Eg.
<input ngModel="..." pattern="{{FIVE_OUT_OF_SEVEN}}" .../>
Is this the right way of going about doing this?
You cannot assign a RegExp to an enum, an enum can either be numeric or string-based.
So when it comes to storing the RegExp you have two options:
Option A - store the RegExp as string in an enum
enum RegExpEnum {
ONE_DANK_REGEX = "^[dank]+$",
FIVE_OUT_OF_SEVEN = "^[meme]{5,7}$"
}
Option B - store the RegExp in a class/variable
class RegexClass {
public static readonly ONE_DANK_REGEX = /^[dank]+$/g;
public static readonly FIVE_OUT_OF_SEVEN = /^[meme]{5,7}$/g;
}
In both ways you need to get a reference to the containing enum or class in your component. Which can be achieved by assigning it to a local variable.
#Component({ ... })
public class MyComponent {
availableRegex = RegExpEnum; // or RegexClass
}
Then you can access it in the html part of your component.
<input [(ngModel)]="name" [pattern]="availableRegex.ONE_DANK_REGEX"/>
DEMO
Enum will support only strings and Number.
If you wanted to store regular expressions, you need to use static class
export class RegularExpression
{
public static ALPHA_CHARACTERS: RegExp = /([^A-Za-z])+/g;
}
Usage
'Your_* Name'.replace(RegularExpression.ALPHA_CHARACTERS, '');
Prefer the option A mentioned on the currently accepted answer and use it as below the code:
export enum Regex {
ONE_DANK_REGEX = "^[dank]+$",
FIVE_OUT_OF_SEVEN = "^[meme]{5,7}$",
}
public readonly regOne = new RegExp(Regex.ONE_DANK_REGEX, "g");
This gives some flexibility when create the regular expression object.
#Query("MATCH (m:Movie)<-[r:ACTED_IN]-(a:Person) RETURN m,r,a LIMIT {limit}")
Collection<Movie> graph(#Param("limit") int limit);
For this query, it's returning "RETURN m, r, a", which is a full subgraph with 3 elements. Then why the return value of 'graph' method is a collection of "Movie" only? Where is the 'r, a' which is also returned.
I am trying to understanding the mechanism behind the scene.
It seems that you have a #RelationshipEntity defined in your class path but do not use it when defining #Relationships in the domain classes.
Sample:
#NodeEntity
class Pet {
// ...
}
#NodeEntity
class Person {
#Relationship(type = "HAS")
private List<Pet> pets;
// ...
}
#RelationshipEntity(type = "HAS")
class HasRelationship {
// ...
}
If Neo4j OGM, that acts behind the scenes of SDN, finds a relationship type, it looks for #RelationshipEntity first and if it finds them, tries to map the returned types back to the #NodeEntity. In this case OGM finds HasRelationship and wants to map it to the Person class. This fails because Person does only know of Pet and the objects get discarded.
Like my answer on GitHub.
I have a JSON object that I am getting from my server that looks something like this:
{
"state":"1",
"player1": {
"alias":"Player Name",
"ready":"0"
}
}
I am able to get the JSON, parse it into a FJsonObject, and retrieve any number or string in the first level of the JSON object using this code to serialize:
TSharedPtr<FJsonObject> JsonParsed;
TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(json);
if (FJsonSerializer::Deserialize(JsonReader, JsonParsed))
//Use JsonParsed
And this code to read strings:
FString AJSONContainer::getStringWithKey(FString key)
{
return storedJSON->GetStringField(key);
}
Side Note:
AJSONContainer is just an Actor class that I use to call these functions from Blueprints.
That's all fine and dandy, but when I try to read things from the second level, things don't work.
I wrote this code to get the next level down:
TSharedPtr<FJsonObject> nested = storedJSON->GetObjectField(key);
But all calls to get fields of nested return nothing.
nested->GetStringField(anotherKey); //Nothing
So, for example, with the above JSON, this:
TSharedPtr<FJsonObject> nested = storedJSON->GetObjectField("player1");
FString alias = nested->GetStringField("alias");
alias has no value when I print it to the console.
Am I doing something wrong? Why isn't the second-level JSON working?
Don't know if you got it sorted out, but I found a pretty nasty function that works for nested objects and, also, for arrays altogether. And it gives you a USTRUCT, so you don't have to use the functions that get values by Keys (I don't like them since they're very error prone). Instead, you'll have type safety!
FJsonObjectConverter::JsonObjectStringToUStruct
Here are the docs and another question answered on UE4 AnswerHub
Basically, you create the target USTRUCT (or USTRUCTs for nested JSONs), mark all properties with UPROPERTY, so Unreal knows their names, and use this function. It will copy the values by matchmaking them. It copies even the arrays! =D
Example
I'll call the JSON FString to be deserialized Json and it's structure is like the one below. It contains a nested object and an array, to make things interesting.
{
"nested" : {
"id" : "654asdf",
"name" : "The Name"
},
"foo" : "foobar",
"bar_arr" : [
{ "barfoo" : "asdf" },
{ "barfoo" : "qwer" }
]
}
Before converting, we need to create the USTRUCTs from inside out (so we can reference inner on the outer). Remember to always use F for struct names.
USTRUCT()
struct FNested
{
GENERATED_USTRUCT_BODY()
UPROPERTY()
FString id;
UPROPERTY()
FString name;
};
USTRUCT()
struct FBar
{
GENERATED_USTRUCT_BODY()
UPROPERTY()
FString barfoo;
};
USTRUCT()
struct FJsonData
{
GENERATED_USTRUCT_BODY()
UPROPERTY()
FNested nested;
UPROPERTY()
FString foo;
UPROPERTY()
TArray<FBar> bar_arr;
};
The conversion will go like this:
FJsonData JsonData;
FJsonObjectConverter::JsonObjectStringToUStruct<FJsonData>(
Json,
&JsonData,
0, 0);
Now, you are able to access all the properties as in standard C++ structs. Eg., to access one of the barfoos:
FString barfoo0 = JsonData.bar_arr[0].barfoo;
I have not tested it with int and float in the JSON, but since it copies even arrays, I believe that would work also.
for (auto currJsonValue = JsonObject->Values.CreateConstIterator(); currJsonValue; ++currJsonValue)
{
// Get the key name
const FString Name = (*currJsonValue).Key;
// Get the value as a FJsonValue object
TSharedPtr< FJsonValue > Value = (*currJsonValue).Value;
TSharedPtr<FJsonObject> JsonObjectIn = Value->AsObject();
}
The Json Object nested can be accessed by GetObjectField or the code I posted.
As I commented calling GetField<EJson::Object> instead of GetObjectField is the solution.
So this code will get your nested json:
TSharedPtr<FJsonValue> nested = storedJSON->GetField<EJson::Object>("player1");
TSharedPtr<FJsonObject> nestedParsed = nested->AsObject();
FString alias = nestedParsed->GetStringField("alias"); // alias == "Player Name"
I have a generic class for making and processing JSON API requests. I pass in the TParam and TResult template parameters but when I use a derived type it's implementation is not being called.
Here is some code you can throw in a playground to illustrate:
import Cocoa
// Base class for parameters to POST to service
class APIParams {
func getData() -> Dictionary<String, AnyObject> {
return Dictionary<String, AnyObject>()
}
}
// Base class for parsing a JSON Response
class APIResult {
func parseData(data: AnyObject?) {
}
}
// Derived example for a login service
class DerivedAPIParams: APIParams {
var user = "some#one.com"
var pass = "secret"
// THIS METHOD IS CALLED CORRECTLY
override func getData() -> Dictionary<String, AnyObject> {
return [ "user": user, "pass": pass ]
}
}
// Derived example for parsing a login response
class DerivedAPIResult: APIResult {
var success = false
var token:String? = ""
// THIS METHOD IS NEVER CALLED
override func parseData(data: AnyObject?) {
/*
self.success = data!.valueForKey("success") as Bool
self.token = data!.valueForKey("token") as? String
*/
self.success = true
self.token = "1234"
}
}
class APIOperation<TParams: APIParams, TResult: APIResult> {
var url = "http://localhost:3000"
func request(params: TParams, done: (NSError?, TResult?) -> ()) {
let paramData = params.getData()
// ... snip making a request to website ...
let result = self.parseResult(nil)
done(nil, result)
}
func parseResult(data: AnyObject?) -> TResult {
var result = TResult.self()
// This should call the derived implementation if passed, right?
result.parseData(data)
return result
}
}
let derivedOp = APIOperation<DerivedAPIParams, DerivedAPIResult>()
let params = DerivedAPIParams()
derivedOp.request(params) {(error, result) in
if result? {
result!.success
}
}
The really weird thing is that only the DerivedAPIResult.parseData() is not called, whereas the DerivedAPIParams.getData() method is called. Any ideas why?
UPDATE: This defect is fixed with XCode 6.3 beta1 (Apple Swift version 1.2 (swiftlang-602.0.37.3 clang-602.0.37))
Added info for a workaround when using XCode 6.1 (Swift 1.1)
See these dev forum threads for details:
https://devforums.apple.com/thread/251920?tstart=30
https://devforums.apple.com/message/1058033#1058033
In a very similar code sample I was having the exact same issue. After waiting through beta after beta for a "fix", I did more digging and discovered that I can get the expect results by making the base class init() required.
By way of example, here is Matt Gibson's reduced example "fixed" by adding the proper init() to ApiResult
// Base class for parsing a JSON Response
class APIResult {
// adding required init() to base class yields the expected behavior
required init() {}
}
// Derived example for parsing a login response
class DerivedAPIResult: APIResult {
}
class APIOperation<TResult: APIResult> {
init() {
// EDIT: workaround for xcode 6.1, tricking the compiler to do what we want here
let tResultClass : TResult.Type = TResult.self
var test = tResultClass()
// should be able to just do, but it is broken and acknowledged as such by Apple
// var test = TResult()
println(test.self) // now shows that we get DerivedAPIResult
}
}
// Templated creation creates APIResult
let derivedOp = APIOperation<DerivedAPIResult>()
I do not know why this works. If I get time I will dig deeper, but my best guess is that for some reason having required init is causing different object allocation/construction code to be generated that forces proper set up of the vtable we are hoping for.
Looks possibly surprising, certainly. I've reduced your case to something rather simpler, which might help to figure out what's going on:
// Base class for parsing a JSON Response
class APIResult {
}
// Derived example for parsing a login response
class DerivedAPIResult: APIResult {
}
class APIOperation<TResult: APIResult> {
init() {
var test = TResult()
println(test.self) // Shows that we get APIResult, not DerivedAPIResult
}
}
// Templated creation creates APIResult
let derivedOp = APIOperation<DerivedAPIResult>()
...so it seems that creating a new instance of a templated class with a type constraint gives you an instance of the constraint class, rather than the derived class you use to instantiate the specific template instance.
Now, I'd say that the generics in Swift, looking through the Swift book, would probably prefer you not to create your own instances of derived template constraint classes within the template code, but instead just define places to hold instances that are then passed in. By which I mean that this works:
// Base class for parsing a JSON Response
class APIResult {
}
// Derived example for parsing a login response
class DerivedAPIResult: APIResult {
}
class APIOperation<T: APIResult> {
var instance: T
init(instance: T) {
self.instance = instance
println(instance.self) // As you'd expect, this is a DerivedAPIResult
}
}
let derivedOpWithPassedInstance = APIOperation<DerivedAPIResult>(instance: DerivedAPIResult())
...but I'm not clear whether what you're trying should technically be allowed or not.
My guess is that the way generics are implemented means that there's not enough type information when creating the template to create objects of the derived type from "nothing" within the template—so you'd have to create them in your code, which knows about the derived type it wants to use, and pass them in, to be held by templated constrained types.
parseData needs to be defined as a class func which creates an instance of itself, assigns whatever instance properties, and then returns that instance. Basically, it needs to be a factory method. Calling .self() on the type is just accessing the type as a value, not an instance. I'm surprised you don't get some kind of error calling an instance method on a type.