I am currently writing an api for a custom application my company is writing. Part of this involves getting published content out in JSON format. When I try serializing ipublishedcontent directly it obviously attempts to serialize all of the umbraco data and relations that I simply don't need (in fact it fails with a stack overflow). Is there a way to get just the custom properties from an item of content without specifying the fields?
I am using webapi and passing it objects to serialize itself and I'm using a dynamic to manually specify the fields. The Product type which I'm initially selecting into is from modelsbuilder. My code currently looks a little like this:
public object Get(string keywords = "")
{
// Get Data from Umbraco
var allProducts = Umbraco.TypedContent(1100).Children.Select(x => new Product(x));
if (keywords != "")
{
allProducts = allProducts.Where(x => x.Name.Contains(keywords));
}
return allProducts.Select(x => new
{
id = x.Id,
name = x.Name,
price = x.Price
});
}
It seems to me that there should be a simple way to do this without having to create a dynamic with just the fields I want but I can't work it out. I just don't want to have to change my code every time the document type in umbraco changes!
You can use Ditto to map your data into an object.
Create an object with properties that match the alias's of your fields (case insensitive)
public class Product{
public int id {get;set;}
public string name {get;set;}
public string price {get;set;}
}
Then map a single or collection of IPublishedContent objects using .As
return allProducts.As<Product>();
You can use the UmbracoProperty attribute to specify the alias too if it is different than you need for your json or use the JsonProperty attribute to change the name on serialize.
Take a look at the code in the MemberListView - it does a similar thing while retrieving Members without knowing in advance what the properties on the MemberType will be:
https://github.com/robertjf/umbMemberListView/blob/master/MemberListView/Models/MemberListItem.cs
For example:
[DataContract(Name = "content", Namespace = "")]
public class MemberListItem
{
// The following properties are "known" - common to all IPublishedContent
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "contentType")]
public IContentType ContentType { get; set; }
// This one contains a list of all other custom properties.
private Dictionary<string, string> properties;
[DataMember(Name = "properties")]
public IDictionary<string, string> Properties
{
get
{
if (properties == null)
properties = new Dictionary<string, string>();
return properties;
}
}
}
MemberListView converts to this from a list of SearchResult using AutoMapper, but you could just as easily map it from IPublishedContent.
Related
I have a situation where I have a core webApi endpoint. I would like to be able to serialize the incoming Json to a Dto. The Dto would contain the necessary fields, but the incoming request may contain additional properties (different for some clients). I need to be able to capture the additional properties as well, but they will only be known at runtime (the records are to be stored in a DocumentDB (Mongo)). I was deserializing to a dynamic object:
[Route("api/Chapter/CreateNewChapter")]
[HttpPost]
public IActionResult CreateNewChapter([FromBody]dynamic incomingJson)
{
dynamic incomingObject = JsonConvert.DeserializeObject<ExpandoObject>(incomingJson.ToString(), new ExpandoObjectConverter());
if (!IsAuthenticated(incomingObject))
return Unauthorized();
var createNewChapter = new CreateNewChapter();
var outgoingJson = createNewChapter.Process(incomingObject);
var resultJson = JsonConvert.SerializeObject(outgoingJson, Formatting.Indented);
return Ok(resultJson);
}
This worked just fine, the problem is that there is no schema or concrete object to use for Swagger, as well as validation on all of the fields was a nightmare.
So ultimately I would like to do something like this:
public class ChapterDto
{
public int ChapterId {get; set};
public string ChapterName {get; set};
)
Then if there are additional properties sent in the request (Json), The properties can be added dynamically at runtime. In addition, I would be adding metadata properties "ParentChapterId" etc.
Should I try to map the incoming json to the dto so I know we have the valid incoming properties, then if that passes map the entire Json object to the dynamic object like I'm doing above? Or is there a better way to achieve this?
You can use JSON extension data. When deserializing, it will map additional properties not found in the DTO to a suitably-attributed dictionary property that you add to the DTO.
Example usage:
public class ChapterDto
{
public int ChapterId { get; set; }
public string ChapterName { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> AdditionalData { get; set; }
}
I have a class that has another class inside, but this class has some fields that i donĀ“t want to show when call this controller.
So, how can i hide ?
I tried to Include and do a Select with a new Class DTO, but not success.
For example:
public class Father
{
public string name {get;set}
public FamilyName familyName {get;set;}
}
public class FamilyName
{
public string name {get;set}
public string sex {get;set}
}
Controller
public IQueryable<Father> GetFathers()
{
return db.Fater;
}
When i call the context Father, i have a Json with name and sex. If i need to just show the field "name", how should I do ?
You are Exposing database entities to the client, The client
receives data that maps directly to your database tables, that's not
always a good idea
You can define a data transfer object (DTO). A DTO is an object that defines how the data will be sent over the network.
DTO Class
public class FatherDTO
{
public string name { get; set; }
}
Controller
public IQueryable<FatherDTO> GetFathers()
{
return new FatherDTO(){ name = db.Fater.name };
}
You can convert to DTOs manually in code. Another option is to use a library like AutoMapper that handles the conversion automatically.
For more details check this link
There are a couple of ways to do so.
1.Create an anon. type with the wanted properties.
// controller
public object GetFathers()
{
return db.Father.Select(f => new
{
name = f.name,
familyName = new { name = f.familyName.name }
});
}
2.Create a DTO or the same class with a new instance using select. In order to do that, you will need the query to execute since EF cannot translate new Father into sql.
// controller
public IEnumerable<Father> GetFathers()
{
return db.Father.ToList().Select(f => new Father
{
name = f.name,
familyName = new FamilyName { name = f.familyName.name }
});
}
Do stuff with the JSON serializer (custom attributes\custom converters etc.)
How Can i OutPut the published Content of a Certain Document Type via Web Api?
Example:
I have a Document Type Called
Comment
its has three Properties "Name, Date, Text"
I Want To output the Values of those Properties to a UmbracoApiController So that I can Use it in other WebSites
any thoughts ? Thanks in Advance
public class publishedContentapiController : UmbracoApiController
{
//What Logic To Put Here In Order to get the Content OF published
// Document Types With the Alias "comment"
}
The below code outputs all documents of type "comment" through the webapi
public class publishedContentapiController : UmbracoApiController
{
public IHttpActionResult GetComments()
{
// Create an UmbracoHelper for retrieving published content
var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
// Get all comments from the Umbraco tree (this is not a optimized way of doing this, since it queries the complete Umbraco tree)
var comments = umbracoHelper.TypedContentAtRoot().DescendantsOrSelf("comment");
// Map the found nodes from IPublishedContent to a strongly typed object of type Comment (defined below)
var mappedComments = comments.Select(x => new Comment{
Name = x.Name, // Map name of the document
Date = x.CreatedTime, // Map createdtime
Text = x.GetPropertyValue<string>("text") // Map custom property "text"
});
return Ok(mappedComments);
}
private class Comment
{
public string Name { get; set; }
public DateTime Date { get; set; }
public string Text { get; set; }
}
}
Diclaimer: Code is untested and obviously needs refactoring
I have a class setup like this:
public class Summary
{
public Geometry geometry { get; set; }
public SummaryAttributes attributes { get; set; }
}
public class SummaryAttributes
{
public int SERIAL_NO { get; set; }
public string District { get; set; }
}
public class Geometry
{
public List<List<List<double>>> paths { get; set; }
}
and i take a json string of records for that object and cram them in there like this:
List<Summary> oFeatures = reportObject.layers[0].features.ToObject<List<Summary>>();
my end goal is to create a csv file so i need one flat List of records to send to the csv writer i have.
I can do this:
List<SummaryAttributes> oAtts = oFeatures.Select(x => x.attributes).ToList();
and i get a nice List of the attributes and send that off to csv. Easy peasy.
What i want though is to also pluck a field off of the Geometry object as well and include that in my final List to go to csv.
So the final List going to the csv writer would contain objects with all of the fields from SummaryAttributes plus the first and last double values from the paths field on the Geometry object (paths[0][0][first] and paths[0][0][last])
It's hard to explain. I want to graft two extra attributes onto the original SummaryAttributes object.
I would be ok with creating a new SummaryAttributesXY class with the two extra fields if that's what it takes.
But i'm trying to avoid creating a new anonymous object and having to delimit every field in the SummaryAttributes class as there are many more than i have listed in this sample.
Any suggestions?
You can select new anonymous object with required fields, but you should be completely sure that paths has at least one item in each level of lists:
var query = oFeatures.Select(s => new {
s.attributes.SERIAL_NO,
s.attributes.District,
First = s.geometry.paths[0][0].First(), // or [0][0][0]
Last = s.geometry.paths[0][0].Last()
}).ToList()
Got it figured out. I include the X and Y fields in the original class definition. When the json gets deserialized they will be null. Then i loop back and fill them in.
List<Summary> oFeatures = reportObject.layers[0].features.ToObject<List<Summary>>();
List<Summary> summary = oFeatures.Select(s =>
{
var t = new Summary
{
attributes = s.attributes
};
t.attributes.XY1 = string.Format("{0} , {1}", s.geometry.paths[0][0].First(), s.geometry.paths[0][1].First());
t.attributes.XY2 = string.Format("{0} , {1}", s.geometry.paths[0][0].Last(), s.geometry.paths[0][1].First());
return t;
}).ToList();
List<SummaryAttributes> oAtts = summary.Select(x => x.attributes).ToList();
I am working on a program that uses Nhibernate to persist objects, and Xml Serialization to import and export data. I can't use the same properties for collections as, for example, Nhibernate needs them to be Ilists, because it has it's own implementation of that interface, and I can't Serialize interfaces. But as I need both properties to be synchronized, I thought I could use two different properties for the same Field. The properties will be according to what I need for each framework, and they will update the Field accrodingly.
So, I have the following field:
private IList<Modulo> modulos;
And the following properties:
[XmlIgnore]
public virtual IList<Modulo> Modulos
{
get { return modulos; }
set { modulos = value; }
}
[XmlArray]
[XmlArrayItem(typeof(Modulo))]
public virtual ArrayList XmlModulos
{
get
{
if (modulos == null) return new ArrayList();
var aux = new ArrayList();
foreach (Modulo m in modulos)
aux.Add(m);
return aux;
}
set
{
modulos = new List<Modulo>();
foreach (object o in value)
modulos.Add((Modulo)o);
}
}
The first one is working perfectly, being quite standard, but I have some problems with the second one. The get is working great as I am having no problems Serializing objects (meaning it correctly takes the info from the field). But when I need to Deserialize, it is not getting all the info. The debugger says that after the Deserialization, the field is not updated (null) and the Property is empty (Count = 0).
The obvious solution would be using two unrelated properties, one for each framework, and passing the information manually when needed. But the class structure is quite complicated and I think there should be a more simple way to do this.
Any Idea how I can modify my property for it to do what I want? Any help will be appreciated.
The short answer is that you cant.
Typically you would create a DTO ( Data transfer object ) separate from your NHibernate objects. For example:
public class PersonDto
{
[XmlAttribute(AttributeName = "person-id")]
public int Id { get; set; }
[XmlAttribute(AttributeName = "person-name")]
public string Name{ get; set; }
}
On your DTO object you only put the properties that you intend to serialize. You than create a DTO from your domain model when you need to serialize one.
There is a great little library called automapper that makes mapping from your domain objects to your dto's pretty straight forward. See: http://automapper.codeplex.com/
Here is an example of a person class that supports mapping to the above DTO.
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
static Person()
{
Mapper.CreateMap<PersonDto, Person>();
Mapper.CreateMap<Person, PersonDto>();
}
public Person(PersonDto dto)
{
Mapper.Map<PersonDto, Person>(dto, this);
}
public PersonDto ToPersonDto()
{
var dto = new PersonDto();
Mapper.Map<Person, PersonDto>(this, dto);
return dto;
}
}