This question already has an answer here:
Dart factory constructor - how is it different to “const” constructor
(1 answer)
Closed 8 years ago.
Below is about a usage of factory constructor from Seth Ladd's blog ' Dart - Trying to understand the value of 'factory' constructor'.
class Symbol {
final String name;
static Map<String, Symbol> _cache = new Map<String, Symbol>();
factory Symbol(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final symbol = new Symbol._internal(name);
_cache[name] = symbol;
return symbol;
}
}
Symbol._internal(this.name);
}
main() {
var x = new Symbol('X');
var alsoX = new Symbol('X');
print(identical(x, alsoX)); // true
}
IMHO, with general constructor, the same effect can be achieved with a subtle difference, but quite simpler.
class Symbol {
static final Map<String, Symbol> cache = {};
final String name;
Symbol(name) {
cache[name] = new Symbol._internal();
}
Symbol._internal();
}
main(){
var a = new Symbol('something');
var b = new Symbol('something');
print(identical(a, b)); // false!
print(Symbol.cache); //{something: Instance of 'Symbol'}
}
As shown above, though the two instances, a & b, are different objects, the effect is all the same as shown in 'print(Symbol.cache); //{something: Instance of 'Symbol'}' as a map object permit only one of same strings as its key.
So, my question was what are peculiar merits of factory constructor(or factory pattern) over general/const constructors? Because the above sample code alone shows no merit of factory constructor.
Could anyone explain what is so called 'Factory Pattern' in Dart language rather than Java/C#?
The factory PATTERN is the same. It's a general pattern and is not language specific.
Dart provides factory constructors to support the factory pattern. The factory constructor is able to return values (objects). In your first example you check if there is an instance for the key you return it.
In the second example you don't check the key of the map and you don't return the instance. That's why the two instances are not identical.
Regards,
Robert
Related
I'm working with some complex json in dart, and I have an issue creating objects before I know what type they'll be.
I appreciate the suggestions, but I don't think I completely understand. In the given answer:
var entity = Model();
castToEntity(entity, {'test': 10});
Don't I need to know that it will be a Model class?
What if I have the below two classes:
#JsonSerializable(explicitToJson: true, includeIfNull: false)
class Location {
String id;
String resourceType;
Location({#required this.id, this.resourceType})
factory Location.fromJson(Map<String, dynamic> json) => _$LocationFromJson(json);
Map<String, dynamic> toJson() => _$LocationToJson(this);
}
class Reference {
String reference;
String resourceType;
Location({#required this.reference, this.resourceType}
factory Reference.fromJson(Map<String, dynamic> json) => _$ReferenceFromJson(json);
Map<String, dynamic> toJson() => _$ReferenceToJson(this);
}
And then I query the server, and I don't know what kind of class it will be. It could be a Location, or a Reference, or if it's a list, it could be multiple of both, and I don't know until I've requested it.
var myBundle = Bundle.fromJson(json.decode(response.body));
Each "myBundle.entry" is another resource. I'd like to be able to use information from that resource to define itself. So I could do something like:
myBundle.entry.resourceType newResource = new myBundle.entry.resourceType();
What I'm doing right now is sending it to a function that has all of the possible options predefined:
var newResource = ResourceTypes(myBundle.entry[i].resource.resourceType,
myBundle.entry[i].resource.toJson());
dynamic ResourceTypes(String resourceType, Map<String, dynamic> json) {
if (resourceType == 'Location') return (new Location.fromJson(json));
if (resourceType == 'Reference') return (new Reference.fromJson(json));
}
It was said that there's not reflection in dart, so I didn't know any other way to do it.
As far as I know, it's not possible since Dart doesn't have Reflection like c#, the most close that I can imagine, is using an abstract class that enforces your entity to implement fromJson, and, in that method, you read the Map and put values into fields, like the code below:
abstract class Serializable {
void fromJson(Map<String,dynamic> data);
}
class Model implements Serializable {
int test;
#override
void fromJson(data) {
test = data['test'];
}
}
Serializable castToEntity(Serializable entity, Map<String, dynamic> data) {
return entity..fromJson(data);
}
Now, when you read you database and have the Map, you can call a method generic like:
var entity = Model();
castToEntity(entity, {'test': 10});
print(entity.test);
Where entity is an empty model.
Note: Your fields on entity, cannot be final, since fromJson is an instance method and not a factory method.
I'm trying to implement the singleton pattern in a database helper class, but, I can't seem to understand the purpose of a factory constructor and if there's an alternative method to using it.
class DbHelper {
final String tblName ='';
final String clmnName ='';
final String clmnPass='';
DbHelper._constr();
static final DbHelper _db = new DbHelper._constr();
factory DbHelper(){ return _db;}
Database _mydb;
Future<Database> get mydb async{
initDb() {
if(_mydb != null)
{
return _mydb;
}
_mydb = await initDb();
return _mydb;
}
There is no need to use the factory constructor.
The factory constructor was convenient when new was not yet optional because then it new MyClass() worked for classes where the constructor returned a new instance every time or where the class returned a cached instance. It was not the callers responsibility to know how and when the object was actually created.
You can change
factory DbHelper(){ return _db;}
to
DbHelper get singleton { return _db;}
and acquire the instance using
var mySingletonReference = DbHelper.singleton;
instead of
var mySingletonReference = DbHelper();
It's just a matter of preference.
I also found this article helpful: https://theburningmonk.com/2013/09/dart-implementing-the-singleton-pattern-with-factory-constructors/
I'm using Java 8 and jackson to try and get an int value from a json object. Here is some similar code I used to verify the structure.
HashMap<String, Object> myMap = new HashMap<String, Object>();
myMap
.entrySet()
.stream()
.forEach(x -> System.out.println(x.getKey() + " => " + x.getValue()));
and the result:
myKey1 => {"number":1}
myKey2 => {"number":1}
myKey3 => {"number":2}
What I'm trying to do is use the key, like myKey1 to find the json value i.e. {"number":1} and pull out the actual number, which for that key is 1.
However, I don't all know the values of the key's, just the key I want to match up. So I have to do this dynamically. I know the structure of the values and that is always the same, except the number can be different.
I think because I don't know the keys and their order, plus I'm using entrySet, that is forcing me into using Optional, which is making this more difficult.
Here is my code where I'm actually using the key to pull the json value:
Optional<Object> object = myMap.entrySet().stream()
.filter(e -> e.getKey().equals(myKey))
.map(Map.Entry::getValue)
.findFirst();
However, the stream pulls back
Optional[{"number":1}]
and I can't seem to get the number out so I can return it from a method. I don't actually
have a Java class for the object, so I assume that is why it's returning Optional as I was getting a compile error without it, but I'm not sure.
Any ideas as to the right way to do this?
Why iterating over all the entries of the map and do a linear search by key, to get the value of the entry?
Just get the value directly:
Object value = myMap.get(myKey);
Now, with regard to the number inside value, as you say you don't have a class that represents the values of the map, the most likely thing is that the JSON library you're using is creating a Map for each value. So from now on, let's assume that the values are actually some implementation of Map:
Integer = null;
if (value != null) {
// What's the type of the value? Maybe a Map?
if (value instanceof Map) {
Map<String, Object> valueAsMap = (Map<String, Object>) value;
number = (Integer) valueAsMap.get("number");
}
}
I'm assuming the numbers are Integers, but they can perfectly be Long instances or even Doubles or BigDecimals. Just be sure of the exact type, so the second cast doesn't fail.
EDIT: For completeness, here's the code for when the values are not maps, but of some class that represents a json node. Here I'm using JsonNode from Jackson library, but the approach is very similar for other libs:
Integer = null;
if (value != null) {
if (value instanceof JsonNode) {
JsonNode valueAsNode = (JsonNode) value;
number = (Integer) valueAsNode.get("number").numberValue();
}
}
findFirst() returns an Optional. You need to call .get() or .orElse() or .orElseThrow() after you call findFirst().
You can then cast the Object to JsonNode and retrieve the value. Here is the full code:-
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
HashMap<String, Object> myMap = new HashMap<String, Object>();
myMap.put("myKey1", mapper.readTree("{\"number\":1}"));
myMap.put("myKey2", mapper.readTree("{\"number\":1}"));
myMap.put("myKey3", mapper.readTree("{\"number\":2}"));
System.out.println(getNumberUsingMapKey(myMap, "myKey3"));
}
private static int getNumberUsingMapKey(Map<String, Object> map, String key) throws Exception {
return Optional.of(map.get(key))
.map(o -> ((JsonNode) o).get("number").asInt())
.get(); //or use one of the following depending on your needs
// .orElse(-1);
// .orElseThrow(Exception::new);
}
//or use this method
private static int getNumberUsingMapKeyWithoutOptional(Map<String, Object> map, String key) throws Exception {
Object o = map.get(key);
return ((JsonNode) o).get("number").asInt();
}
Output
2
Unfortunately I didn't describe my problem statement well from the start, but this is the final working code that I was able to piece from the other answers.
This is what the data structure looked like. A key that had a value which was a JSON object that had a key and an int, in other words a nested JSON object.
Key: myKey1 Value:{"number":1}
Key: myKey2 Value:{"number":1}
Key: myKey3 Value:{"number":2}
I'm posting it in case some else runs into this use case. This may not be the best way to do it, but it works.
Object value = myMap.get(keyName);
ObjectMapper mapper = new ObjectMapper();
String jsonString = (String) value;
JsonNode rootNode = mapper.readTree(jsonString);
JsonNode numberNode = rootNode.path("number");
System.out.println("number: " + numberNode.intValue());
and the result:
number: 1
Is there a way to make DataContractJsonSerializer emit the "odata.type" field required when posting an OData entity into a collection that supports multiple entity types (hierarchy per table)?
If I construct DataContractJsonSerializer with a settings object with EmitTypeInformation set to Always, it emits a "__type" field in the output, but that's not the field name needed for OData and the format of the value is wrong as well.
Is there any way to hook into the DataContractJsonSerializer pipeline to inject the desired "odata.type" field into the serialization output?
It would be such a hack to have to parse the serialization output in order to inject the field. How does WCF Data Services do it? Not using DataContractJsonSerializer is my guess.
Have you considered using Json.Net? Json.Net is much more extensible and the scenario that you have can be done using a custom resolver. sample code
class Program
{
static void Main(string[] args)
{
Console.WriteLine(
JsonConvert.SerializeObject(new Customer { Name = "Raghu" }, new JsonSerializerSettings
{
ContractResolver = new CustomContractResolver()
}));
}
}
public class CustomContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract objectContract = base.CreateObjectContract(objectType);
objectContract.Properties.Add(new JsonProperty
{
PropertyName = "odata.type",
PropertyType = typeof(string),
ValueProvider = new StaticValueProvider(objectType.FullName),
Readable = true
});
return objectContract;
}
private class StaticValueProvider : IValueProvider
{
private readonly object _value;
public StaticValueProvider(object value)
{
_value = value;
}
public object GetValue(object target)
{
return _value;
}
public void SetValue(object target, object value)
{
throw new NotSupportedException();
}
}
}
public class Customer
{
public string Name { get; set; }
}
I can't answer your first two questions, but for the third question, I found on the OData Team blog a link to the OData WCF Data Services V4 library open source code. Downloading that code, you will see that they perform all serialization and deserialization manually. They have 68 files in their two Json folders! And looking through the code they have comments such as:
// This is a work around, needTypeOnWire always = true for client side:
// ClientEdmModel's reflection can't know a property is open type even if it is, so here
// make client side always write 'odata.type' for enum.
So that to me kind of implies there is no easy, clean, simple, elegant way to do it.
I tried using a JavaScriptConverter, a dynamic type, and other stuff, but most of them ended up resorting to using Reflection which just made for a much more complicated solution versus just using a string manipulation approach.
recently I have set up a WCF restful service with EF4.
It all worked out when returning XML format response. however when it comes to JSON, I got 504 Error. unable to return json data, WCF Resful Service .NET 4.0
By digging deeper by using Service Trace Viewer:
I found this error:
'The type 'xxx.DataEntity.AppView'
cannot be serialized to JSON because
its IsReference setting is 'True'. The
JSON format does not support
references because there is no
standardized format for representing
references. To enable serialization,
disable the IsReference setting on the
type or an appropriate parent class of
the type.'
The "AppView" is a complex object class which generated by EF4 from a store procedure.
I spend quite a bit time google how to disable the IsReference, very little result so far.
anyone? with any solutions?
thanks in advance
Code:
[OperationContract]
[WebInvoke(Method = "GET",
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "App/{id}/{format}")]
AppView FuncDetail(string id, string format);
public AppView FuncDetail(string id, string format)
{
SetResponseFormat(format);
return AppSvcs.GetById(id);
}
private void SetResponseFormat(string format)
{
if (format.ToLower() == "json")
{
ResponseContext.Format = WebMessageFormat.Json;
}
else
{
ResponseContext.Format = WebMessageFormat.Xml;
}
}
I ran into exactly the same issue. It only happened on one of my service methods where I was trying to return JSON serialised Entity objects. For all my other methods I was returning JSON serialised data transfer objects (DTOs), which are stand-alone and not connected to the Entity framework. I am using DTOs for data posted into methods. Often, the data you send out does not need all the data you store in the model or the database e.g. ID values, updated dates, etc. The mapping is done in the model class, like so:
public partial class Location
{
public static LocationDto CreateLocationDto(Location location)
{
LocationDto dto = new LocationDto
{
Accuracy = location.Accuracy,
Altitude = location.Altitude,
Bearing = location.Bearing
};
return dto;
}
It may seem a bit clunky but it works and it ensures that you only send the data fields you intended to send back. It works for me because I only have 5 or 6 entities but I can see that it would get a bit tedious if you have lots of classes.
I was running into the same problem, as caused by using the auto-generated ADO Entity Models. I have not found a direct fix for this issue, but as a work around, I serialize the response as json explicitly.
So in your example, the AppView FuncDetail looks like this:
public object FuncDetail(string id, string format)
{
SetResponseFormat(format);
// where AppSvc is the object type and the enumerable list of this type is returned by the GetById method, cast it to a json string
return JSONSerializer.ToJson<AppSvc>(AppSvcs.GetById(id));
}
Here are the Serializers that I'm using:
public static class GenericSerializer
{
public static DataTable ToDataTable<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
// column names
PropertyInfo[] oProps = null;
if (varlist == null) return dtReturn;
foreach (T rec in varlist)
{
// Use reflection to get property names, to create table, Only first time, others will follow
if (oProps == null)
{
oProps = ((Type)rec.GetType()).GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition()
== typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
}
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
{
dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue
(rec, null);
}
dtReturn.Rows.Add(dr);
}
return dtReturn;
}
}
public static class JSONSerializer
{
public static string ToJson<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = GenericSerializer.ToDataTable(varlist);
return GetJSONString(dtReturn);
}
static object RowsToDictionary(this DataTable table)
{
var columns = table.Columns.Cast<DataColumn>().ToArray();
return table.Rows.Cast<DataRow>().Select(r => columns.ToDictionary(c => c.ColumnName, c => r[c]));
}
static Dictionary<string, object> ToDictionary(this DataTable table)
{
return new Dictionary<string, object>
{
{ table.TableName, table.RowsToDictionary() }
};
}
static Dictionary<string, object> ToDictionary(this DataSet data)
{
return data.Tables.Cast<DataTable>().ToDictionary(t => "Table", t => t.RowsToDictionary());
}
public static string GetJSONString(DataTable table)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(table.ToDictionary());
}
public static string GetJSONString(DataSet data)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(data.ToDictionary());
}}
It is a lot clearer to use Entity Metadata instead of Reflection.
The Metadata is pretty extensive.
another way to do this is to use LINQ to create an anonymous type with the subset of fields that you need from your entity and then use JSON.NET to serialize the collection of anon types that you created in the LINQ statement. then persist that collection out as a string by serializing.