In YamlDotNet: Is there a way to output a null value as an empty string in a sequence? - yamldotnet

When writing a sequence in an IYamlTypeConverter you might use some code like this:
public class MyObjectConverter : IYamlTypeConverter {
public MyObjectConverter() {}
public bool Accepts(Type type) { return typeof(IMyObject) == type || typeof(IMyObject[]) == type; }
public object ReadYaml(IParser parser, Type type) { return null; }
public void WriteYaml(IEmitter emitter, object value, Type type) {
var itemVal = value as IMyObject;
if (itemVal != null)
emitter.Emit(new Scalar(itemVal.GetID()));
else {
var arrayVal = value as IMyObject[];
emitter.Emit(new SequenceStart(null, null, true, SequenceStyle.Block));
if (arrayVal != null) {
foreach (var item in arrayVal)
if (item != null) emitter.Emit(new Scalar(item.GetID()));
else emitter.Emit(new Scalar("null"));
}
emitter.Emit(new SequenceEnd());
}
}
}
By calling emitter.Emit(new Scalar("null")) you would get a 'null' entry in the sequence, but if you leave the serialization up to YamlDotNet, it would be serialized as '' (empty string).
How do you output a null value in a sequence as an empty string when writing a custom IYamlTypeConverter?

One way to achieve this is to create a custom IEventEmitter that will add this logic:
public class NullStringsAsEmptyEventEmitter : ChainedEventEmitter
{
public NullStringsAsEmptyEventEmitter(IEventEmitter nextEmitter)
: base(nextEmitter)
{
}
public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter)
{
if (eventInfo.Source.Type == typeof(string) && eventInfo.Source.Value == null)
{
emitter.Emit(new Scalar(string.Empty));
}
else
{
base.Emit(eventInfo, emitter);
}
}
}
You then register it like this:
var serializer = new SerializerBuilder()
.WithEventEmitter(nextEmitter => new NullStringsAsEmptyEventEmitter(nextEmitter))
.Build();
Here's a fiddle with this code

It seems you can represent a null value simply with '~', according to http://www.yaml.org/refcard.html

Related

Entity Framework Core 6 - trim all returned strings by command interception

When I was working with EF (System.Data.Entity) I successfully used an interceptor to automatically trim all existing strings in the database.
The IDbCommandTreeInterceptor is described in this post: EF6.1–Workaround Trailing Blanks Issue in String Joins.
public class StringTrimmerInterceptor : IDbCommandTreeInterceptor
{
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace == DataSpace.SSpace)
{
var queryCommand = interceptionContext.Result as DbQueryCommandTree;
if (queryCommand != null)
{
var newQuery = queryCommand.Query.Accept(new StringTrimmerQueryVisitor());
interceptionContext.Result = new DbQueryCommandTree(
queryCommand.MetadataWorkspace,
queryCommand.DataSpace,
newQuery);
}
}
}
private class StringTrimmerQueryVisitor : DefaultExpressionVisitor
{
private static readonly string[] _typesToTrim = { "nvarchar", "varchar", "char", "nchar" };
public override DbExpression Visit(DbNewInstanceExpression expression)
{
var arguments = expression.Arguments.Select(a =>
{
var propertyArg = a as DbPropertyExpression;
if (propertyArg != null && _typesToTrim.Contains(propertyArg.Property.TypeUsage.EdmType.Name))
return EdmFunctions.Trim(a);
return a;
});
return DbExpressionBuilder.New(expression.ResultType, arguments);
}
}
}
I need some help to implement the same functionality by EntityFrameworkCore DbCommandInterceptor.

Could haxe macro be used to detect when object is dirty (any property has been changed)

Let say we have an object:
#:checkDirty
class Test {
var a:Int;
var b(default, default):String;
var c(get, set):Array<Int>;
public function new() {
...
}
public function get_c() {
...
}
public function set_c(n) {
...
}
}
Could we write a macro checkDirty so that any change to field/properties would set property dirty to true. Macro would generate dirty field as Bool and clearDirty function to set it to false.
var test = new Test();
trace(test.dirty); // false
test.a = 12;
trace(test.dirty); // true
test.clearDirty();
trace(test.dirty); //false
test.b = "test"
trace(test.dirty); //true
test.clearDirty();
test.c = [1,2,3];
trace(test.dirty); //true
Just to note - whenever you consider proxying access to an object, in my experience, there are always hidden costs / added complexity. :)
That said, you have a few approaches:
First, if you want it to be pure Haxe, then either a macro or an abstract can get the job done. Either way, you're effectively transforming every property access into a function call that sets the value and also sets dirty.
For example, an abstract using the #:resolve getter and setter can be found in the NME source code, replicated here for convenience:
#:forward(decode,toString)
abstract URLVariables(URLVariablesBase)
{
public function new(?inEncoded:String)
{
this = new URLVariablesBase(inEncoded);
}
#:resolve
public function set(name:String, value:String) : String
{
return this.set(name,value);
}
#:resolve
public function get(name:String):String
{
return this.get(name);
}
}
This may be an older syntax, I'm not sure... also look at the operator overloading examples on the Haxe manual:
#:op(a.b) public function fieldRead(name:String)
return this.indexOf(name);
#:op(a.b) public function fieldWrite(name:String, value:String)
return this.split(name).join(value);
Second, I'd just point out that if the underlying language / runtime supports some kind of Proxy object (e.g. JavaScript Proxy), and macro / abstract isn't working as expected, then you could build your functionality on top of that.
I wrote a post (archive) about doing this kind of thing (except for emitting events) before - you can use a #:build macro to modify class members, be it appending an extra assignment into setter or replacing the field with a property.
So a modified version might look like so:
class Macro {
public static macro function build():Array<Field> {
var fields = Context.getBuildFields();
for (field in fields.copy()) { // (copy fields so that we don't go over freshly added ones)
switch (field.kind) {
case FVar(fieldType, fieldExpr), FProp("default", "default", fieldType, fieldExpr):
var fieldName = field.name;
if (fieldName == "dirty") continue;
var setterName = "set_" + fieldName;
var tmp_class = macro class {
public var $fieldName(default, set):$fieldType = $fieldExpr;
public function $setterName(v:$fieldType):$fieldType {
$i{fieldName} = v;
this.dirty = true;
return v;
}
};
for (mcf in tmp_class.fields) fields.push(mcf);
fields.remove(field);
case FProp(_, "set", t, e):
var setter = Lambda.find(fields, (f) -> f.name == "set_" + field.name);
if (setter == null) continue;
switch (setter.kind) {
case FFun(f):
f.expr = macro { dirty = true; ${f.expr}; };
default:
}
default:
}
}
if (Lambda.find(fields, (f) -> f.name == "dirty") == null) fields.push((macro class {
public var dirty:Bool = false;
}).fields[0]);
return fields;
}
}
which, if used as
#:build(Macro.build())
#:keep class Some {
public function new() {}
public var one:Int;
public var two(default, set):String;
function set_two(v:String):String {
two = v;
return v;
}
}
Would emit the following JS:
var Some = function() {
this.dirty = false;
};
Some.prototype = {
set_two: function(v) {
this.dirty = true;
this.two = v;
return v;
}
,set_one: function(v) {
this.one = v;
this.dirty = true;
return v;
}
};

Adding filters to data views with IEnumerable functions

I am working on customization to add some filters to the existing data view.
The Activity data view on the Employee Time Activities page.
I have added the Customer property to PMTimeActivity and OwnedFilter.
Now I need to modify the activity method to take into consideration the Customer filter.
The only way to do this is to override the method with one of the following scenarios:
without calling the base method and copying the code and adding the
filter part
with calling the base method and checking the condition on each
returned record.
The first scenario is making this part of customization very problematic because it will require to review this code every time the customization is being upgraded to any other build.
The second scenario is not good from a performance view.
Has anybody faced this issue and how can this be done in an acceptable way?
Below is the code of the activity method:
protected virtual IEnumerable activity()
{
List<object> args = new List<object>();
EmployeeActivitiesEntry.PMTimeActivityFilter filterRow = this.Filter.Current;
if (filterRow == null)
{
return null;
}
BqlCommand cmd = BqlCommand.CreateInstance(new Type[]
{
typeof(Select2<EPActivityApprove, LeftJoin<EPEarningType, On<EPEarningType.typeCD, Equal<PMTimeActivity.earningTypeID>>, LeftJoin<CRActivityLink, On<CRActivityLink.noteID, Equal<PMTimeActivity.refNoteID>>, LeftJoin<CRCase, On<CRCase.noteID, Equal<CRActivityLink.refNoteID>>, LeftJoin<ContractEx, On<CRCase.contractID, Equal<ContractEx.contractID>>>>>>, Where<EPActivityApprove.ownerID, Equal<Current<EmployeeActivitiesEntry.PMTimeActivityFilter.ownerID>>, And<EPActivityApprove.trackTime, Equal<True>, And<PMTimeActivity.isCorrected, Equal<False>>>>, OrderBy<Desc<EPActivityApprove.date>>>)
});
if (filterRow.ProjectID != null)
{
cmd = cmd.WhereAnd<Where<EPActivityApprove.projectID, Equal<Current<EmployeeActivitiesEntry.PMTimeActivityFilter.projectID>>>>();
}
if (filterRow.ProjectTaskID != null)
{
cmd = cmd.WhereAnd<Where<EPActivityApprove.projectTaskID, Equal<Current<EmployeeActivitiesEntry.PMTimeActivityFilter.projectTaskID>>>>();
}
if (filterRow.FromWeek != null || filterRow.TillWeek != null)
{
List<Type> cmdList = new List<Type>();
bool? includeReject = filterRow.IncludeReject;
bool flag = true;
if (includeReject.GetValueOrDefault() == flag & includeReject != null)
{
cmdList.Add(typeof(Where<, , >));
cmdList.Add(typeof(EPActivityApprove.approvalStatus));
cmdList.Add(typeof(Equal<ActivityStatusListAttribute.rejected>));
cmdList.Add(typeof(Or<>));
}
if (filterRow.FromWeek != null)
{
if (filterRow.TillWeek != null)
{
cmdList.Add(typeof(Where<, , >));
}
else
{
cmdList.Add(typeof(Where<, >));
}
cmdList.Add(typeof(EPActivityApprove.weekID));
cmdList.Add(typeof(GreaterEqual<Required<EmployeeActivitiesEntry.PMTimeActivityFilter.fromWeek>>));
args.Add(filterRow.FromWeek);
if (filterRow.TillWeek != null)
{
cmdList.Add(typeof(And<>));
}
}
if (filterRow.TillWeek != null)
{
cmdList.Add(typeof(Where<EPActivityApprove.weekID, LessEqual<Required<EmployeeActivitiesEntry.PMTimeActivityFilter.tillWeek>>>));
args.Add(filterRow.TillWeek);
}
cmd = cmd.WhereAnd(BqlCommand.Compose(cmdList.ToArray()));
}
return new PXView(this, false, cmd).SelectMultiBound(new object[]
{
this.Filter.Current
}, args.ToArray());
}
I would consider to try work with PXView, which was described by Sergey here.
In case of Employee activities time card it may look like this:
public class EmployeeExt : PXGraphExtension<EmployeeActivitiesEntry>
{
protected virtual IEnumerable activity()
{
var sel = new PXView(Base, true, Base.Activity.View.BqlSelect);
if(true)
{
sel.WhereAnd<Where<EPActivityApprove.projectID, Equal<Current<EmployeeActivitiesEntry.PMTimeActivityFilter.projectID>>>>();
}
int totalRow = 0;
int startRow = PXView.StartRow;
return sel.Select(PXView.Currents, PXView.Parameters,
PXView.Searches, PXView.SortColumns, PXView.Descendings,
PXView.Filters, ref startRow, PXView.MaximumRows, ref totalRow);
}
}

Who owns returned string from _bstr_t::wchar_t*, _bstr_t::char* operators?

_bstr_t::wchar_t*, _bstr_t::char* operators return string of different types.
Do I need to delete or free them? using which function?
After stepping the implementation using debugger, my conclusion is that there is no need to manually delete/free the returned string. The lifetime of the returned string is managed by _bstr_t internally.
See the following snippets from the implementation:
// Extract a const char_t*
//
inline _bstr_t::operator const char*() const throw(_com_error)
{
return (m_Data != NULL) ? m_Data->GetString() : NULL;
}
inline const char* _bstr_t::Data_t::GetString() const throw(_com_error)
{
if (m_str == NULL) {
m_str = _com_util::ConvertBSTRToString(m_wstr);
if (m_str == NULL && m_wstr != NULL) {
_com_issue_error(E_OUTOFMEMORY);
}
}
return m_str;
}
inline void _bstr_t::Data_t::_Free() throw()
{
if (m_wstr != NULL) {
::SysFreeString(m_wstr);
}
if (m_str != NULL) {
delete [] m_str;
}
}
It is also okay to use unnamed _bstr_t as follows because _bstr_t instance is destroyed after the constructor of CString has finished.
CString abc((LPCTSTR)_bstr_t(OLESTR("ABC")));
AfxMessageBox(abc);

Casting on run time using implicit con version

I have the following code which copies property values from one object to another objects by matching their property names:
public static void CopyProperties(object source, object target,bool caseSenstive=true)
{
PropertyInfo[] targetProperties = target.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
PropertyInfo[] sourceProperties = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo tp in targetProperties)
{
var sourceProperty = sourceProperties.FirstOrDefault(p => p.Name == tp.Name);
if (sourceProperty == null && !caseSenstive)
{
sourceProperty = sourceProperties.FirstOrDefault(p => p.Name.ToUpper() == tp.Name.ToUpper());
}
// If source doesn't have this property, go for next one.
if(sourceProperty ==null)
{
continue;
}
// If target property is not writable then we can not set it;
// If source property is not readable then cannot check it's value
if (!tp.CanWrite || !sourceProperty.CanRead)
{
continue;
}
MethodInfo mget = sourceProperty.GetGetMethod(false);
MethodInfo mset = tp.GetSetMethod(false);
// Get and set methods have to be public
if (mget == null)
{
continue;
}
if (mset == null)
{
continue;
}
var sourcevalue = sourceProperty.GetValue(source, null);
tp.SetValue(target, sourcevalue, null);
}
}
This is working well when the type of properties on target and source are the same. But when there is a need for casting, the code doesn't work.
For example, I have the following object:
class MyDateTime
{
public static implicit operator DateTime?(MyDateTime myDateTime)
{
return myDateTime.DateTime;
}
public static implicit operator DateTime(MyDateTime myDateTime)
{
if (myDateTime.DateTime.HasValue)
{
return myDateTime.DateTime.Value;
}
else
{
return System.DateTime.MinValue;
}
}
public static implicit operator MyDateTime(DateTime? dateTime)
{
return FromDateTime(dateTime);
}
public static implicit operator MyDateTime(DateTime dateTime)
{
return FromDateTime(dateTime);
}
}
If I do the following, the implicit cast is called and everything works well:
MyDateTime x= DateTime.Now;
But when I have a two objects that one of them has a DateTime and the other has MyDateTime, and I am using the above code to copy properties from one object to other, it doesn't and generate an error saying that DateTime can not converted to MyTimeDate.
How can I fix this problem?
One ghastly approach which should work is to mix dynamic and reflection:
private static T ConvertValue<T>(dynamic value)
{
return value; // This will perform conversion automatically
}
Then:
var sourceValue = sourceProperty.GetValue(source, null);
if (sourceProperty.PropertyType != tp.PropertyType)
{
var method = typeof(PropertyCopier).GetMethod("ConvertValue",
BindingFlags.Static | BindingFlags.NonPublic);
method = method.MakeGenericMethod(new[] { tp.PropertyType };
sourceValue = method.Invoke(null, new[] { sourceValue });
}
tp.SetValue(target, sourceValue, null);
We need to use reflection to invoke the generic method with the right type argument, but dynamic typing will use the right conversion operator for you.
Oh, and one final request: please don't include my name anywhere near this code, whether it's in comments, commit logs. Aargh.

Resources