Dotliquid cannot bind property of an object inside an array - asp.net-core-2.0

I have a .NET Core application that is using dotliquid. From the try online it looks like I can bind a property of an object that is inside an array. Like {{user.tasks[0].name}} where tasks is a collection of task object and name is property of the task.
I have JSON model that would be the input to the template. I don't know the JSON structure during the design time. So I am converting JSON string into ExpandoObject.
However, this does not work when I bind property of an object that is inside an array.
Demo NETFiddle
public class Program
{
public static void Main()
{
// this does not work
var modelString = "{\"States\": [{\"Name\": \"Texas\",\"Code\": \"TX\"}, {\"Name\": \"New York\",\"Code\": \"NY\"}]}";
var template = "State Is:{{States[0].Name}}";
Render(modelString,template);
//this works
modelString = "{\"States\": [\"Texas\",\"New York\"]}";
template = "State Is:{{States[0]}}";
Render(modelString,template);
}
private static void Render(string modelString, string template)
{
var model = JsonConvert.DeserializeObject<ExpandoObject>(modelString);
var templateModel = Hash.FromDictionary(model);
var html = Template.Parse(template).Render(templateModel);
Console.WriteLine(html);
}
}

You should parse as Dictionary<string, IEnumerable<ExpandoObject>> but not ExpandoObject.
using System;
using DotLiquid;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using System.Dynamic;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
// this does not work when binding a property of an object that is inside collection and when use dictionary
var modelString = "{\"States\": [{\"Name\": \"Texas\",\"Code\": \"TX\"}, {\"Name\": \"New York\",\"Code\": \"NY\"}]}";
var template = "State Is:{{Name}}";
RenderFromDictionary(modelString, template);
}
private static void RenderFromDictionary(string modelString, string template)
{
var Stats = JsonConvert.DeserializeObject<Dictionary<string, IEnumerable<ExpandoObject>>>(modelString);
foreach (ExpandoObject expandoObject in Stats["States"])
{
var templateModel = Hash.FromDictionary(expandoObject);
var html = Template.Parse(template).Render(templateModel);
Console.WriteLine(html);
}
}
}
Test Result:

Related

How to set the namespace of marshalled xml using camel jaxb?

For starters, I'm creating some routes using Camel ver 2.15 (in Fuse 6.2.1).
In my route, i'm trying to create a XML from a pojo that was generated using cxf-xjc maven plugin (cxf-xjc read some xsd somewhere then from the xsd, the pojos with jaxb annotations were produced).
The pojos are TempProject and TempProjects.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(
name = "",
propOrder = {"ecode", "tempName"}
)
#XmlRootElement(
name = "TempProject"
)
public class TempProject implements Serializable {
#XmlElement(
name = "Ecode",
required = true
)
protected String ecode;
#XmlElement(
name = "TempName",
required = true
)
protected String tempName;
public TempProject() {
}
public String getEcode() {
return this.ecode;
}
public void setEcode(String value) {
this.ecode = value;
}
public String getTempName() {
return this.tempName;
}
public void setTempName(String value) {
this.tempName = value;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(
name = "",
propOrder = {"tempProjects"}
)
#XmlRootElement(
name = "TempProjects"
)
public class TempProjects implements Serializable {
#XmlElement(
name = "TempProject",
required = true
)
protected List<TempProject> tempProjects;
public TempProjects() {
}
public List<TempProject> getTempProjects() {
if (this.tempProjects == null) {
this.tempProjects = new ArrayList();
}
return this.tempProjects;
}
}
I can generate the xml using this code:
JAXBContext jaxbContext = JAXBContext.newInstance(new Class[]{TempProjects.class});
jaxbContext.createMarshaller();
JaxbDataFormat jaxbDataFormat = new JaxbDataFormat(jaxbContext); //import org.apache.camel.converter.jaxb.JaxbDataFormat;
I call
.marshal(jaxbDataFormat)
in my route to effect the marshalling from the pojo to xml.
The generated xml is posted below:
<TempProjects xmlns="http://blah.blah/foo/schema/v2">
<TempProject>
<Ecode>1</Ecode>
<TempName>Tempname1</TempName>
</TempProject>
<TempProject>
<Ecode>2</Ecode>
<TempName>Tempname2</TempName>
</TempProject>
How can i generate a marshalled xml that will have a namespace like this...
<TempProjects xmlns:myprefix="http://blah.blah/foo/schema/v2">
Reason being why I needed a namespaceprefix is because I plan to split the values (e.g. Ecode) in the xml using xpath and I needed a namespaceprefix to do that (thats what ive researched, i might be wrong).
My planned code in my route is
.marshal(jaxbDataFormat)
.split( xpath("/TempProjects/TempProject/Ecode/text()").namespaces(ns1),
new ProjectIdsAggregator()) //the above xpath doesn't work because it doesn't have a namespace prefix
//Namespaces ns1 = new Namespaces("myprefix", "http://blah.blah/foo/schema/v2" );
I looked at jaxbDataFormat.setNamespacePrefixRef("myprefix"), but i got an error (org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: myprefix of type: java.util.Map)
I'm actually quite new in the apache camel routing world, so i might be missing some basic stuff.
You don't need to change your XML at all. It is fine.
With the XML you posted and the Namespace declaration you posted, the following XPath works fine to split the XML (as an example) into two TempProject parts:
xpath("/myprefix:TempProjects/myprefix:TempProject").namespaces(ns1)
Because you declared the XML namespace like this:
Namespaces ns1 = new Namespaces("myprefix", "http://blah.blah/foo/schema/v2" )
Your XPath must use the prefix myprefix for all elements:
/myprefix:TempProjects/myprefix:TempProject

Identifier 'Submission#0' is not CLS-compliant in Azure functions

I hope somebody could help me on this one. I am implementing an Azure Function where I am trying to serialise an XML message into .Net object. This is the code that I am currently using:
public static void Run(string input, TraceWriter log)
{
    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(App));
    // more code here....
}
public class App
{
    public string DataB { get; set; }
}
However, I always got this error:
2017-01-17T12:21:35.173 Exception while executing function: Functions.ManualXmlToJson. mscorlib: Exception has been thrown by the target of an invocation. System.Xml: Identifier 'Submission#0' is not CLS-compliant.
Parameter name: ident.
I have tried with XmlAttributes, without them. I added the buildOptions:warningsAsErrors as false in project.json file but nothing happens. And to be honest, I ran out of ideas because this code is actually working in an App Console.
I guess is some parameter of something, I would really appreciate if somebody can suggest me how to fix it.
Thanks!
Your best option here will be to factor the class you're attempting to serialize into a separate class library and reference that from your function.
If you implement your App class above in a different assembly, your function code would look like the following:
#r "<yourassemblyname>.dll"
using System;
using <YourClassNamespace>;
public static void Run(string input, TraceWriter log)
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(App));
}
The code above assumes a private assembly reference, where you upload your assembly to a bin folder, inside of your function folder.
You can find more about external references here: https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-csharp#referencing-external-assemblies
I'm opening an issue to address the CLS compliant name so this is not as confusing:
https://github.com/Azure/azure-webjobs-sdk-script/issues/1123
Another option worth trying (which would minimize the changes you'd need to make to your code) is to use the DataContractSerializer instead. You can find more information here.
Here is a quick sample of a function using the DataContractSerializer (with your type above):
#r "System.Runtime.Serialization"
using System;
using System.Xml;
using System.Runtime.Serialization;
public static void Run(string input, TraceWriter log)
{
string xml = WriteObject(new App { DataB = "Test"});
log.Info(xml);
}
[DataContract(Name = "App")]
public class App
{
[DataMember]
public string DataB { get; set; }
}
public static string WriteObject(App app)
{
using (var output = new StringWriter())
using (var writer = new XmlTextWriter(output) { Formatting = Formatting.Indented })
{
var serializer = new DataContractSerializer(typeof(App));
serializer.WriteObject(writer, app);
return output.GetStringBuilder().ToString();
}
}

Using Object Initializers dynamically

I need to pass a list of integers to a stored procedure because Entity Framework takes too long to process the request. I'm using a User Defined Table Type to do this. I'm using EntityFrameworkExtras.EF6 and I've created a stored procedure and Table Type class to help with this. Here are those classes:
namespace MyModel{
using EntityFrameworkExtras.EF6;
[UserDefinedTableType("SelectedActivity")]
public class ChartCountryUDT
{
[UserDefinedTableTypeColumn(1)]
public int ID { get; set; }
}
[StoredProcedure("GetCountryChartData")]
public class ChartCountryStoredProcedure
{
[StoredProcedureParameter(System.Data.SqlDbType.Udt, ParameterName = "ActivityIDs")]
public List<ChartCountryUDT> ChartCountryUDT { get; set; }
}}
and here is my method to call the stored procedure passing in the Table Type and returning me a List of objects:
public List<ChartCountry> GetCountriesForChart(List<int> activityIDs)
{
using (MyEntities ctx = new MyEntities())
{
var procedure = new ChartCountryStoredProcedure()
{
ChartCountryUDT = new List<ChartCountryUDT>()
{
new ChartCountryUDT() {ID = 1 }
}
};
return (List<ChartCountry>)ctx.Database.ExecuteStoredProcedure<ChartCountry>(procedure);
}
}
As you can see in my ChartCountryUDT object initializer, I'm hardcoding one object by setting the ID value to 1. This works fine but I would like to take the activityIDs parameter that is passed in and create new objects in my object initializer for each ID in the activityIDs parameter. Is there any way of looping trough my list of activityIDs and creating new objects in my object initializer for each record?
Thanks
You are basically asking how to map (convert) List<int> to List<ChartCountryUDT>, which in LINQ is called projection (select):
var procedure = new ChartCountryStoredProcedure()
{
ChartCountryUDT = activityIDs.Select(id => new ChartCountryUDT { ID = id }).ToList()
};

deserialize SortedSet : why items need to implement IComparable?

I have de folowing classes :
[DataContract]
public class MyProject
{
[DataMember(Name = "Branches")]
private SortedSet<ModuleFilter> branches = new SortedSet<ModuleFilter>(new ModuleFilterComparer());
[DataMember(Name="VbuildFilePath")]
private string buildprogram = null;
}
I can serialize it to a file with :
DataContractSerializer x = new DataContractSerializer(p.GetType());
using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(p.GetFilePath()))
{
x.WriteObject(writer, p);
}
But when I try to read it back with the folowing piece of code, it fails unless I add a dummy implementation of IComparable to the ModuleFilter object
DataContractSerializer x = new DataContractSerializer(typeof(MyProject));
using (System.Xml.XmlReader reader = System.Xml.XmlReader.Create(filePath))
{
p = (MyProject)x.ReadObject(reader);
}
Why does not the deserializer use the provided IComparer of the SortedSet member ?
Thank you
It is because DataContractSerializer uses default constructor of SortedSet to initialize field.
Solution 1: recreate field after deserialization with needed comparer
[DataContract]
public class MyProject : IDeserializationCallback
{
//...
void IDeserializationCallback.OnDeserialization(Object sender)
{
branches = new SortedSet<ModuleFilter>(branches, new ModuleFilterComparer());
}
}
Solution 2: use your own sorted set implementation instead of SortedSet<ModuleFilter>
public class ModuleFilterSortedSet : SortedSet<ModuleFilter>
{
public ModuleFilterSortedSet()
: base(new ModuleFilterComparer())
{
}
public ModuleFilterSortedSet(IComparer<ModuleFilter> comparer)
: base(comparer)
{
}
}

Help With Error C#: An object reference is required for the non-static field, method, or property RpgTutorial.Character.Swordsmanship

I am very new to C# and programming in general and I'm having the error (described in the title box) when I run this code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
namespace RpgTutorial
{
public class HeroSkills : Character
{
public int Skill()
{
if (Hero.Swordsmanship = 10)
{
}
}
}
}
Now I know I need to create a reference to Swordsmanship, but how exactly would I do that? Thank you for any help!
If you're trying to access the Swordsmanship property of the same object that the method would be called for, then you can access it via the this reference:
if (this.Swordsmanship == 10)
{
...
}
Is a Hero a subclass of Character (or the other way around)? If so, you can reference the property Swordsmanship like this:
if (this.Swordsmanship == 10)
{
...
}
Otherwise if you are finding yourself needing to reference a 'hero', you can add a constructor (and property) to your HeroSkills class like this:
public HeroSkills : Character
{
public Hero CurrentHero
{
get;
set;
}
public HeroSkills(Hero hero)
{
this.CurrentHero = hero;
}
...
Note that the this keyword is not required, but signifies that the property you are accessing is a member of your class. This can help you in readability later on. You can then reference the CurrentHero around your class in your various methods like the Skill() as so:
if (this.CurrentHero.Swordsmanship == 10)
{
...
}
You would use your newly modified class elsewhere in code like this:
Hero player1 = //some hero variable
var skills = new HeroSkills(player1);
int currentSkill = skills.Skill();

Resources