how to write condition using j-easy easy-rules to find any matching value in a set - mvel

I would like to write a condition to evaluate if any one of the set values in the facts, meets the condition.
This is my rule definition:
public void evaluateRule(Facts facts, String ruleNum) {
SpRuleDefinition spRuleDefinition = spRuleDefinitionService.findByRuleNum(ruleNum);
System.out.println("Invoke Action : " + spRuleDefinition.getActionNum());
facts.put("action", "ACTION1");
MVELRule rule = new MVELRule()
.name("Rule1")
.description("My Rule")
.priority(1)
.when("shipment.orgNum=='ORG1' && (shipment.fromAddress.country=='IN' || shipment.toAddress.country=='IN') && shipment.shipmentLines.itemDetail.active==false")
.then("shipment.setOutcome(action);");
Rules rules = new Rules();
rules.register(rule);
//Fire rules on known facts
RulesEngine rulesEngine = new DefaultRulesEngine();
rulesEngine.fire(rules, facts);
}
The input that I am passing can be something like this:
{"orgNum": "ORG1", "fromAddress": { "country": "SGP"}, "shipmentLines": [{ "itemDetail": {"active": true, "countryOfOrigin": "IN"}, "itemNum": "I1", "quantity": 10 }, { "itemDetail": {"active": false, "countryOfOrigin": "US"}, "itemNum": "I2", "quantity": 1 }],"toAddress": { "country": "IN"}}
I would like to evaluate if any one of the shipment lines has itemDetail that has the active flag set to false.
The above rule fails with the following exception:
org.mvel2.PropertyAccessException: [Error: could not access: itemDetail; in class: java.util.HashSet]
[Near : {... s.country=='IN') && shipment.shipmentLines.itemDet ....}
]

That's a MVEL question rather than an EasyRules question.
I would like to evaluate if any one of the shipment lines has itemDetail that has the active flag set to false.
You can define a function that iterates over the set and checks your condition. Here is an example:
#Test
public void testConditionOnSet() {
Set<ItemDetail> itemDetailSet = new HashSet<>();
itemDetailSet.add(new ItemDetail(false));
itemDetailSet.add(new ItemDetail(false));
itemDetailSet.add(new ItemDetail(true));
String condition = "active = false;\n" +
"foreach (itemDetail : itemDetails) {\n" +
" active = active && itemDetail.active;\n" +
"}\n" +
"System.out.println(\"Is there a non active itemDetail?: \" + !active);";
Map facts = new HashMap();
facts.put("itemDetails", itemDetailSet);
Object result = MVEL.eval(condition, facts);
}
static class ItemDetail {
private boolean active;
public ItemDetail(boolean active) {
this.active = active;
}
public boolean isActive() {
return active;
}
}
This example prints: Is there a non active itemDetail? true.
Hope this helps.

Related

Compare multiple fields of Object to those in an ArrayList of Objects

I have created a 'SiteObject' which includes the following fields:
data class SiteObject(
//Site entry fields (10 fields)
var siteReference: String = "",
var siteAddress: String = "",
var sitePhoneNumber: String = "",
var siteEmail: String = "",
var invoiceAddress: String = "",
var invoicePhoneNumber: String = "",
var invoiceEmail: String = "",
var website: String = "",
var companyNumber: String = "",
var vatNumber: String = "",
)
I want to filter an ArrayList<SiteObject> (call it allSites) by checking if any of the fields of the objects within the list match those in a specific <SiteObject> (call it currentSite).
So for example, I know how to filter looking at one field:
fun checkIfExistingSite(currentSite: SiteObject) : ArrayList<SiteObject> {
var matchingSites = ArrayList<SiteObject>()
allSites.value?.filter { site ->
site.siteReference.contains(currentSite.siteReference)}?.let { matchingSites.addAll(it)
}
return matchingSites
}
But I am looking for an elegant way to create a list where I compare the matching fields in each of the objects in allSites with the corresponding fields in currentSite..
This will give me a list of sites that may be the same (allowing for differences in the way user inputs data) which I can present to the user to check.
Use equals property of Data Class:
val matchingSites: List<SiteObject> = allSites
.filterNotNull()
.filter { it.equals(currentSite) }
If you are looking for a more loose equlity criteria than the full match of all fields values, I would suggest usage of reflection (note that this approach could have performance penalties):
val memberProperties = SiteObject::class.memberProperties
val minMatchingProperties = 9 //or whatever number that makes sense in you case
val matchingItems = allSites.filter {
memberProperties.atLeast(minMatchingProperties) { property -> property.get(it) == property.get(currentSite) }
}
fun <E> Iterable<E>.atLeast(n: Int, predicate: (E) -> Boolean): Boolean {
val size = count()
return when {
n == 1 -> this.any(predicate)
n == size -> this.all(predicate)
n > size - n + 1 -> this.atLeast(size - n + 1) { !predicate.invoke(it) }
else -> {
var count = 0
for (element in this) {
if (predicate.invoke(element)) count++
if (count >= n) return true
}
return false
}
}
}
you could specify all the fields by which you want to match the currentSite inside the filter predicate:
fun checkIfExistingSite(currentSite: SiteObject) =
allSites.filter {
it.siteAddress == currentSite.siteAddress
|| it.sitePhoneNumber == currentSite.sitePhoneNumber
|| it.siteReference == currentSite.siteReference
}
Long but fast solution because of short circuiting.
If the list is nullable you can transform it to a non nullable list like:
allSites?filter{...}.orEmpty()
// or imho better
allSites.orEmpty().filter{...}

How do I prevent the extra separator?

Given the code ST4 C# code below, how do I prevent the trailing separator (',') from appearing in my output? The trailing separator won't appear if there aren't items to skip after the first item is output (e.g. if the third item in the list isn't present). So, clearly the problem has to do with skipping items, but it doesn't have a problem skipping the first item (e.g. no leading separator before outputting the second item).
2 is even,
static void Main (string [] args)
{
List <Item> items = new List <Item> () { new Item ("1", false), new Item ("2", true), new Item ("3",false)};
string string_template_group =
#"
even_params (parameters) ::=
<<
<parameters:{p|<if (p.is_even)><p.value> is even<endif>}; separator = "", "">
>>
";
TemplateGroup group = new TemplateGroupString (string_template_group);
Template even_params = group.GetInstanceOf ("even_params");
even_params.Add ("parameters", items);
Console.WriteLine (even_params.Render ());
} // End Main
} // End class Program
class Item
{
public string value { get; set; }
public bool is_even { get; set; }
public Item (string V, bool E)
{
value = V;
is_even = E;
}
} // End class Item
Changing the separator to "sep" (as requested by #Har) provides the expected output:
2 is evensep

Moq + xunit + asp.net core: service return null value

I use xUnit as test runner in my asp.net core application.
Here is my test theory:
[Theory(DisplayName = "Search advisors by advisorId"),
ClassData(typeof(SearchAdvisorsByIdTestData))]
public async void SearchAdvisors_ByAdvisorId(int brokerDealerId, FilterParams filter)
{
// Arrange
var _repositoryMock = new Mock<IRepository>();
// Do this section means we bypass the repository layer
_repositoryMock
.Setup(x => x.SearchAdvisors(filter.CID.Value, new AdvisorSearchOptions
{
SearchKey = filter.SeachKey,
AdvisorId = filter.AdvisorId,
BranchId = filter.BranchId,
City = filter.City,
Skip = filter.Skip,
Limit = filter.Limit,
RadiusInMiles = filter.Limit,
Longitude = filter.Longitude,
Latitude = filter.Latitude
}))
.Returns(Task.FromResult<SearchResults<Advisor>>(
new SearchResults<Advisor>()
{
Count = 1,
Limit = 0,
Skip = 0,
ResultItems = new List<SearchResultItem<Advisor>>() {
//some initialize here
}
})
);
_advisorService = new AdvisorService(_repositoryMock.Object, _brokerDealerRepositoryMock, _brokerDealerServiceMock);
// Action
var model = await _advisorService.Search(brokerDealerId, filter);
Assert.True(model.AdvisorResults.Count == 1);
Assert.True(model.AdvisorResults[0].LocationResults.Count > 0);
}
The service like this
public async Task<ViewModelBase> Search(int brokerDealerId, FilterParams filter)
{
var opts = new AdvisorSearchOptions
{
SearchKey = filter.SeachKey,
AdvisorId = filter.AdvisorId,
BranchId = filter.BranchId,
City = filter.City,
Skip = filter.Skip,
Limit = filter.Limit,
RadiusInMiles = filter.Limit,
Longitude = filter.Longitude,
Latitude = filter.Latitude
};
var searchResults = await _repository.SearchAdvisors(filter.CID.Value, opts); // line 64 here
if (searchResults.Count == 0 && Utils.IsZipCode(filter.SeachKey))
{
}
//Some other code here
return model;
}
The issue was after run line 64 in the service. I always get null value of searchResults although I already mocked _repository in the test.
What was my wrong there?
Thank in advance.
Argument matcher for SearchAdvisors() mock does not work because you pass different instances of AdvisorSearchOptions. First instance is created in _repositoryMock.Setup() statement and the second one is created in Search() method itself.
There are several ways to fix this problem:
1.If you don't care about verifying whether instance of AdvisorSearchOptions passed to repository is filled correctly, just use It.IsAny<AdvisorSearchOptions>() matcher in mock setup:
_repositoryMock.Setup(x => x.SearchAdvisors(filter.CID.Value, It.IsAny<AdvisorSearchOptions>()))
.Returns(/*...*/);
2.In previous case the test will not verify that AdvisorSearchOptions is filled correctly. To do this, you could override Object.Equals() method in AdvisorSearchOptions class so that mock call will match for different instances:
public class AdvisorSearchOptions
{
// ...
public override bool Equals(object obj)
{
var cmp = obj as AdvisorSearchOptions;
if (cmp == null)
{
return false;
}
return SearchKey == cmp.SearchKey && AdvisorId == cmp.AdvisorId &&
/* ... compare all other fields here */
}
}
3.Another way to verify object passed to mock is to save the instance via Mock callback and then compare required fields:
AdvisorSearchOptions passedSearchOptions = null;
_repositoryMock
.Setup(x => x.SearchAdvisors(filter.CID.Value, It.IsAny<AdvisorSearchOptions>()))
.Returns(Task.FromResult<SearchResults<Advisor>>(
new SearchResults<Advisor>()
{
Count = 1,
Limit = 0,
Skip = 0,
ResultItems = new List<SearchResultItem<Advisor>>() {
//some initialize here
}
})
)
.Callback<int, AdvisorSearchOptions>((id, opt) => passedSearchOptions = opt);
// Action
// ...
Assert.IsNotNull(passedSearchOptions);
Assert.AreEqual(filter.SearchKey, passedSearchOptions.SearchKey);
Assert.AreEqual(filter.AdvisorId, passedSearchOptions.AdvisorId);
// Check all other fields here
// ...

How to use GroupFormatter with ObjectListView control

I cannot seem to find anywhere, any examples on how to make use of the GroupFormatter delegate to allow me to add footers to my groups when using the ObjectListView control.
Does anyone have any examples that could demonstrate this? I want to remove the text from the group header and add a footer (different text per footer). As well as changing font, etc.
Any examples would be very helpful.
You can analyze the code for the
public void MakeGroupies<T>(T[] values, string[] descriptions, object[] images, string[] subtitles, string[] tasks)
method of the ObjectListView class. That explicitly sets the GroupKeyGetter, GroupKeyToTitleConverter and GroupFormatter property delegates.
This is C# but your VB adaptation should be straightforward. I am using this small test class as the object type to bind to the list view.
public class TestClass
{
private readonly string _s;
private readonly float _f;
public TestClass( string p1, float p2 )
{
this._s = p1;
this._f = p2;
}
[OLVColumn(DisplayIndex = 1, Name="S", Title="String")]
public string S {get {return this._s;}}
[OLVColumn( DisplayIndex = 2, Name = "F", Title = "Float" )]
public float F {get {return this._f;}}
}
So as not to manually define column traits I am using attributes inside the bound object and a
BrightIdeasSoftware.Generator.GenerateColumns( this.olv, typeof( TestClass ) );
call in the form/user control where I am using the list view. In fact here is the method that completely isolates ObjectListView configuration:
void SetData( TestClass[] objects )
{
// build list columns
Generator.GenerateColumns( this.olv, typeof( TestClass ) );
// use groups and make current column the priimary sort column
this.olv.ShowGroups = true;
this.olv.SortGroupItemsByPrimaryColumn = false;
// loop through columns and set properties
foreach( OLVColumn col in this.olv.Columns )
{
col.Groupable = true;
col.Sortable = true;
if( col.Name == "F" )
{
col.MakeGroupies<float>( new float[] { 10f, 100f, 1000f }, new string[] { "<10", "10-100", "100-1000", ">1000" } );
}
else if( col.Name == "S" )
{
col.UseInitialLetterForGroup = false;
//
col.GroupKeyGetter = ( obj ) =>
{
TestClass tc = (TestClass)obj;
switch( char.ToLower( tc.S[0] ) )
{
case 'a':
case 'e':
case 'i':
case 'o':
case 'u': return true;
default: return false;
}
};
//
col.GroupKeyToTitleConverter = ( o ) => { bool b = (bool)o; return b ? "vowel" : "consonant"; };
//
col.GroupFormatter = ( /*OLVGroup*/ group, /*GroupingParameters*/ parms ) =>
{
string s = string.Format ("{0} {1}", group.GroupId, group.Id);
//group.BottomDescription = "BottomDescription: " + s;
//group.TopDescription = "TopDescription: " + s;
group.Footer = "Footer: " + s;
};
}
}
//
this.olv.RebuildColumns();
//
this.olv.SetObjects( objects );
}
You will definitely have one different footer per each group.

How do I invoke WriteJson recursively?

I use Json.Net.
When I serialize a Department2 object and WriteJson() is invoked I want it to be recursively invoked with each of the Telephone2 objects like I do in ReadJson().
How do I do that?
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public interface ISimpleDatabag
{
string Databag { get; set; }
}
[JsonConverter(typeof(JsonDataBagCreationConverter<Department2>))]
public class Department2
{
public Telephone2[] Phones { get; set; }
}
[JsonConverter(typeof(JsonDataBagCreationConverter<Telephone2>))]
public class Telephone2
{
public string Name { get; set; }
public string AreaCode { get; set; }
public string Number { get; set; }
}
public class JsonDataBagCreationConverter<T> : JsonConverter where T : new()
{
// Json.Net version 4.5.7.15008
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// When I serialize Department and this function is invoked
// I want it to recursively invoke WriteJson with each of the Telephone objects
// Like I do in ReadJson
// How do I do that?
T t = (T)value;
serializer.Serialize(writer, t.GetType());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var target = Create(objectType, jsonObject);
serializer.Populate(jsonObject.CreateReader(), target); // Will call this function recursively for any objects that have JsonDataBagCreationConverter as attribute
return target;
}
protected T Create(Type objectType, JObject jsonObject)
{
return new T();
}
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
}
private void Form1_Load(object sender, EventArgs e)
{
string jsonInput = "{\"Name\": \"Seek4\" , \"CustomDepartmentData\": \"This is custom department data\", \"Phones\":[ {\"Name\": \"A\", \"AreaCode\":444, \"Number\":11111111} ,{\"Name\": \"B\", \"AreaCode\":555, \"Number\":987987987}, {\"Name\": \"C\", \"AreaCode\":222, \"Number\":123123123, \"CustomPhoneData\": \"This is custom phone data\"} ] }";
Department2 objDepartment2 = JsonConvert.DeserializeObject<Department2>(jsonInput); // Yes, it works well
Array.Reverse(objDepartment2.Phones);
string jsonNoDatabag = JsonConvert.SerializeObject(objDepartment2);
}
I ended up controlling the entire process myself, using this huge (not refactored) function.
I basically investigate each of the properties of the object to serialize and then serialize it property by property.
Then I can do custom things on each property
/// <summary>
/// Serializes an object by merging its current values into its databag and returns the databag
/// </summary>
/// <param name="objectToSerialize"></param>
/// <returns>the current values merged into the original databag</returns>
/// <remarks>Jan Nielsen, 01-10-2012</remarks>
internal static string SerializeObjectToDatabag(object objectToSerialize)
{
// You have to do it property by property instead of just serializing the entire object and merge it into the original
// because the object might contain lists of objects with custom data and these list might have been sorted differently from when they were loaded
// So you cannot merge them properly unless you do it on a per listitem basis.
// Which is what I do here.
try
{
if (objectToSerialize == null) // If you ie serialize an empty object in an array
{
return null;
}
string updatedDatabag = "";
bool isIDataBag = objectToSerialize is IDataBag;
if (isIDataBag)
{
updatedDatabag = ((IDataBag)objectToSerialize).Data == null ? "" : ((IDataBag)objectToSerialize).Data.ToString();
// updatedDatabag = ((IDataBag)objectToSerialize).Data.ToString(); // Save original data in a local variable. This is the one we will merge new values into
}
string result = "";
// Now iterate through the objects properties
// Possible properties:
// Simple types: string, int, bool etc: their current value should be overwritten in the databag
// types that implement IDatabag: they should be sent to this function recursively so their possible customdata is not overwritten
// but instead their simple values are merged into their own databag. Then the result of this single property merge is overwritten in the outer objects databag
// Types that are not simple and don't implement IDatabag but have properties that implement IDatabag
// types that are not simple and don't implement IDatabag and don't have any properties in any depth that implement IDatabag: They are overwritten in the databag
// Types that are arrays:
// If the types in the array are simple types (string, bool etc) the entire array property is overwritten in the databag
// If the types in the array implement IDatabag each object is sent recursively to this function and their databag is updated via merge
// Then the entire array is overwritten in the outer objects databag
// Types that are generic list are treated like arrays
var properties = objectToSerialize.GetType().GetProperties();
// In order to be able to deserialize abstract classes and interfaces, we need to serialize the classname with the class
// the deserializer recognizes the word $type followed by a type, when its is invoked with a serializerSettings of
// serializerSettings.TypeNameHandling = TypeNameHandling.Objects;
string name = objectToSerialize.GetType().AssemblyQualifiedName;
string shortName = RemoveAssemblyDetails(name);
bool addedType = false;
foreach (var propertyInfo in properties)
{
if (propertyInfo.Name.ToLower() != "data") // Just skip Databag. Databag is not a "real" property but the contents of all the properties when the object was loaded + possible custom data
{
if (!addedType)
{
string jsonSingleProperty = "{ " + ToCustomJson("$type") + " : " + ToCustomJson(shortName) + " }";
// Merge the current value (jsonSingleProperty) into the databag (that might already have been updated with the values of other properties)
// and update the current result with the new values. Ie "Name" : "Seek4" is updated to "Name" : "Seek4Cars" in the databag
// and the function will now use the updated databag to merge the other properties into
updatedDatabag = MergeDefault(jsonSingleProperty, updatedDatabag, true);
addedType = true;
}
// propertyInfo.Name.ToLower().Contains("struct")
var value = propertyInfo.GetValue(objectToSerialize, null); // This gets the value of the specified property in the current object
isIDataBag = value is IDataBag; // Update for the current object. Note that ie an array of IDatabag will return false here, because array is not IsimpleDatabag
// Basically we should just check if the property implements IDatabag
// But the simpletype check is faster because I don't have to check for the interfaces on ie a string, int etc.
// This branch takes care of 3 cases:
// 1) it is a simple type, ie int
// 2) value is null
// 3) it is an array with a value of null
// If an array with values enters this branch of code the values of the array will be appended, overwritten
// Therefore arrays are treated below in a special case. Unless they are null
// GeneralFunctions.IsExtendedSimpleType_AllTypes(propertyInfo.PropertyType) returns true on ie string[], but only arrays with a value of null should be handled here
// This first check originally just checked for simple types
// Then it became extended simple types ie non-simple types that only contains simple types ie List<int,int>
// But not arrays that must be handled separately
// Then it also handled null values
// And then a special case was made for arrays that are null
if ((GeneralFunctions.IsExtendedSimpleType_AllTypes(propertyInfo.PropertyType) || value == null) && (!propertyInfo.PropertyType.IsArray || (propertyInfo.PropertyType.IsArray && value == null)))
{
// You have to merge even though it is default value.
// If you have ie a bool that has an initial value of true and you deliberately sets it to false
// You want the defaultvalue of false to be merged into the json.
string jsonSingleProperty = "{" + ToCustomJson(propertyInfo.Name) + " : " + ToCustomJson(value) + "}"; // ie {"Name" : "Seek4Cars"}
// Merge the current value (jsonSingleProperty) into the databag (that might already have been updated with the values of other properties)
// and update the current result with the new values. Ie "Name" : "Seek4" is updated to "Name" : "Seek4Cars" in the databag
// and the function will now use the updated databag to merge the other properties into
updatedDatabag = MergeDefault(jsonSingleProperty, updatedDatabag, true);
continue;
}
if (isIDataBag) // ie PhoneSingle. A single property of type IDataBag
{
// Invoke recursively
// First check if this is an object with all null values
bool allPropertiesAreNull = true; // Maybe this should in the future be expanded with a check on if the property has its default value ie an int property with a value of 0
foreach (var propertyInfoLocal in value.GetType().GetProperties())
{
var valueLocal = propertyInfoLocal.GetValue(value, null);
if (valueLocal != null)
{
allPropertiesAreNull = false;
break;
}
}
var testjson = "";
if (allPropertiesAreNull)
{
result = "{" + ToCustomJson(propertyInfo.Name) + " : " + " { } }";
}
else
{
testjson = ToCustomJson(value);
result = "{" + ToCustomJson(propertyInfo.Name) + " : " + SerializeObjectToDatabag(value) + "}";
}
updatedDatabag = MergeDefault(result, updatedDatabag, true);
continue;
}
bool containsIDataBag = CheckForDatabagInterfaces.ImplementsInterface(propertyInfo.PropertyType, "idatabag"); // Check if anything inside the property implements IDatabag ie an array of IDatabag
if (containsIDataBag)
{
// Check if it is somekind of generic list (List<T>, Dictionary<T,T) etc) and if it is a type of ignoreTypes ie List<entity>)
if (value.GetType().IsGenericType && value.GetType().GetGenericArguments().Length > 0)
{
string listValuesAsJson = "";
if (value is IEnumerable)
{
listValuesAsJson += "{ " + ToCustomJson(propertyInfo.Name) + " : [";
bool containsItems = false;
foreach (var element in (IEnumerable)value)
{
containsItems = true;
var current = SerializeObjectToDatabag(element);
if (current != null) // If you serialize an empty array element it is null
{
listValuesAsJson += current + ", "; // Add , between each element
}
}
if (containsItems)
{
listValuesAsJson = listValuesAsJson.Substring(0, listValuesAsJson.Length - 2) + "] }"; // remove last , and add ending ] for the array and add a } because this property is flowing in the free
}
else // No items in value
{
listValuesAsJson += "] }"; // add ending ] for the array and add a } because this property is flowing in the free
}
}
else // A single, generic KeyValuePair property
{
listValuesAsJson += "{ " + ToCustomJson(propertyInfo.Name) + " : ";
listValuesAsJson += SerializeObjectToDatabag(value);
listValuesAsJson += " }";
}
updatedDatabag = MergeDefault(listValuesAsJson, updatedDatabag, false);
}
else if (value.GetType().IsArray)
{
string arrayValuesAsJson = "{ " + ToCustomJson(propertyInfo.Name) + " : [";
bool containsItems = false;
foreach (var element in (Array)value)
{
// Treat them the same way you treat any other object
var current = SerializeObjectToDatabag(element);
if (current != null) // If you serialize an empty array element it is null
{
containsItems = true;
arrayValuesAsJson += current + ", ";
}
}
if (containsItems)
{
arrayValuesAsJson = arrayValuesAsJson.Substring(0, arrayValuesAsJson.Length - 2) + "] }"; // remove last , and add ending ] for the array and add a } because this property is flowing in the free
}
else // No items in value
{
arrayValuesAsJson += "] }"; // add ending ] for the array and add a } because this property is flowing in the free
}
updatedDatabag = MergeDefault(arrayValuesAsJson, updatedDatabag, false);
}
else if ( value.GetType().BaseType != null && value.GetType().BaseType.FullName.ToLower().Contains("system.collections.objectmodel"))
{
// This branch was made specifically to take care of the Media collection of a Seek4.Entities.V2.Media.MediaCollection
var genericList = (IList)value;
int counter = genericList.Count;
string listAsJson = "{ " + ToCustomJson(propertyInfo.Name) + " : [";
if (counter == 0)
{
listAsJson += "] }"; // Ie { "Media": [] }
}
else
{
foreach (var obj in genericList)
{
var current = SerializeObjectToDatabag(obj);
listAsJson += current + ", ";
}
listAsJson = listAsJson.Substring(0, listAsJson.Length -2) + " ] }" ;
}
updatedDatabag = MergeDefault(listAsJson, updatedDatabag, true); // hvordan gør json.net dette med standard?
}
else // a single Non-IDatabag property that contains Idatabag properties
{
string tempResult = "{ " + ToCustomJson(propertyInfo.Name) + " : ";
tempResult += SerializeObjectToDatabag(value) + " }";
updatedDatabag = MergeDefault(tempResult, updatedDatabag, true);
}
}
else
{
if (value.GetType().IsArray) // This is an array of simple types so just overwrite
{
string arrayAsJson = "{ " + ToCustomJson(propertyInfo.Name) + " : ";
arrayAsJson += ToCustomJson(value) + "}";
updatedDatabag = MergeDefault(arrayAsJson, updatedDatabag, false);
}
else // ie an object that is not simpledatabag and doesn't contain simple databag
{
string jsonSingleProperty = "{" + ToCustomJson(propertyInfo.Name) + " : " + ToCustomJson(value) + "}";
updatedDatabag = MergeDefault(jsonSingleProperty, updatedDatabag, true);
}
}
}
}
return updatedDatabag;
}
catch (Exception ex)
{
string message = ex.Message;
string stack = ex.StackTrace;
throw;
}
}
internal static string ToCustomJson(object objectToConvertToJson)
{
try
{
// Distinguished from Mongodb.Bson.ToJson() extensionmethod by Custom name
JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
serializerSettings.TypeNameHandling = TypeNameHandling.Objects; // Adds a $type on all objects which we need when it is abstract classes and interfaces
IgnoreDataMemberContractResolver contractResolver = new IgnoreDataMemberContractResolver(null, true, true);
serializerSettings.ContractResolver = contractResolver;
serializerSettings.DefaultValueHandling = DefaultValueHandling.Ignore;
IsoDateTimeConverter converter = new IsoDateTimeConverter();
serializerSettings.Converters.Add(converter);
string result = JsonConvert.SerializeObject(objectToConvertToJson, Formatting.None, serializerSettings);
return result;
}
catch (Exception ex)
{
throw new Exception("Error in ToCustomJson: " + ex.Message, ex);
}
}