I have the following xml:
<state>
<groups>
<group id='1' name='Basic Search Options'>
<control name='Building' label='In' display='true' configurable='false'/>
<control name='SearchType' label='For' display='true' configurable='false'/>
<control id='1' default='C' name='Search By' label='By'>
<option searchtype='C' searchmode='Cnumber' value='CNumber' label='C Number' display='true'/>
<option searchtype='C' searchmode='crossrefnumber' value='CNumber1' label='Cross Reference Number' display='true'/>
<option searchtype='P' searchmode='' value='CaseNumber' label='P Name' display='true'/>
<option searchtype='P' searchmode='' value='CaseNumber' label='A Name' display='false'/>
</control>
</group>
<group id='2' name='Advanced Search Options'>
<control name='Ctatus' label='C Status' display='true'/>
<control name='DateFiled' label='Date Filed' display='true'/>
</group>
</groups>
How would I de-serialize this into the following object? I dont want my xml to have the following tags "ArrayofGroup", instead the xml should have custom tags like mention above.
public class GroupOfControls
{
public int instanceId { get; set; }
public int GroupId { get; set; }
public string Name { get; set; }
public List<SearchControl> Group { get; set; }
}
public class SearchControl
{
public string Name { get; set; }
public string Label { get; set; }
public bool Display { get; set; }
public string Default { get; set; }
public List<SearchOption> SearchOptions { get; set; }
}
public class SearchOption
{
public string Value { get; set; }
public string Label { get; set; }
public bool Display { get; set; }
public string SearchMode { get; set; }
public string SearchType { get; set; }
}
}
If you don't have an XSD file, you need to create one from your XML. You can do this with the Visual Studio Command Line using the following command:
xsd myfilename.xml
Once you have an XSD file this should be easy enough.
I am working with Visual Studio 2010 (C#/.Net 4) and I would do this:
First I would make a new solution in Visual Studio:
Then you need to import the XSD file into your project by right clicking on your solution, and selecting Add => Existing Item and browsing to the XSD.
Once you have the XSD inside your project you need to launch the Visual Studio command prompt, use cd to navigate to your projects directory and then type the following command:
xsd myFilename.xsd /classes
This generates the C# class you are going to deserialize your XML into. Use the Add Existing Item dialogue to import this new class into your solution.
Next you add using System.Xml.Serialization; and using System.IO; to your using statements. Then use the following code to deserialize your XML to an object (assuming the XML validates against your XSD). The most recent XSD I could file I called ResponseData as shown below:
With this in mind, my C# code is below:
using System;
using System.IO;
using System.Xml.Serialization;
namespace XML_Deserialization
{
class Program
{
static void Main(string[] args)
{
ResponseData myResponseData = new ResponseData();
XmlSerializer mySerializer = new XmlSerializer(typeof(ResponseData));
StreamReader myStreamReader = new StreamReader(#"C:\Users\JMK\Documents\Visual Studio 2010\Projects\scratch\XML Deserialization\XML Deserialization\text.xml");
myResponseData = (ResponseData)mySerializer.Deserialize(myStreamReader);
Console.ReadLine();
}
}
}
Related
I have a select field
<select asp-for="UserCitizenships" asp-items="Model.CitizenshipsList" class="select2 required">
that is populated by a multiselect list
public MultiSelectList CitizenshipsList { get; set; }
And returns a List of Int
public List<int> UserCitizenships
that I handle manually in the backend. Validation from Data Annotations is not working. I need at least one property to be populated when the form submits. Any ideas?
[Required(ErrorMessage = Helpers.ErrorMessages.Required)]
public List<int> UserCitizenships
{
get
{
....
I would have thought the following would work
[Required, MinLength(1, ErrorMessage = "At least one item required")]
public List<int> UserCitizenships { get; set; }
Alternatively you could create a custom data annotation or use class level validation
public class YourClass : IValidatableObject
{
[Required]
List<int> UserCitizenships
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (UserCitizenships.Count < 1)
{
yield return new ValidationResult(
$"At least one UserCitizenship should be specified.",
new[] { nameof(UserCitizenships) });
}
}
}
I want to configure per XML a module with a property, which contains a list of class MyObject.
My Module and MyObject class are looking like:
public class MyModule : Module
{
public IList<MyObject> MyObjects { get; set; }
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
// Do something with MyObjects.
}
}
public class MyObject
{
public string Id { get; set; }
public bool IsVisable { get; set; }
}
My assumption was to configure it like this in XML:
<modules name="MyModule">
<type>...</type>
<properties>
<MyObjects>
<MyObject>
<Id>1234</Id>
<IsVisilble>false</IsVisilble>
</MyObject>
</MyObjects>
</properties>
But if I run this, I get the following exception:
Unable to convert object of type 'Autofac.Configuration.Util.ConfiguredDictionaryParameter' to type 'System.Collections.Generic.IList`1[MyObject]'
I'm using Autofac 4.5.0 with Autofac.Configuration 4.0.1.
What am I doing wrong? Is there a way to get it working?
I am trying to serialize some data I have into this XML format but not able to achive the same.
The Desired XML output is below:
<?xml version="1.0" encoding="utf-8"?>
<Root xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Datas>
<Data xmlns="" i:type="DataA">
<Name>A1</Name>
<ADesc>Description for A</ADesc>
</Data>
<Data xmlns="" i:type="DataB">
<Name>B1</Name>
<BDesc>Description for b</BDesc>
</Data>
</Datas>
</Root>
The Classes I created for serialization are as follows:
public class Data
{
[XmlElement("Name")]
public string Name { get; set; }
}
public class DataA : Data
{
[XmlElement("ADesc")]
public string ADesc { get; set; }
}
public class DataB : Data
{
[XmlElement("BDesc")]
public string BDesc { get; set; }
}
[XmlRoot("Root")]
public class Root
{
[XmlArray("Datas")]
[XmlArrayItem(Type = typeof(Data))]
[XmlArrayItem(Type = typeof(DataA))]
[XmlArrayItem(Type = typeof(DataB))]
public List<Data> Datas { get; set; }
}
I use the below method for serializing:
internal static string Serialize(Root obj)
{
var ns = new XmlSerializerNamespaces();
ns.Add("i", "http://www.w3.org/2001/XMLSchema-instance");
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Root));
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, obj, ns);
return textWriter.ToString();
}
}
But the output I get is this (which is not correct):
<?xml version="1.0" encoding="utf-8"?>
<Root xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Datas>
<DataA>
<Name>A1</Name>
<ADesc>Description for A</ADesc>
</DataA>
<DataB>
<Name>B1</Name>
<BDesc>Description for b</BDesc>
</DataB>
</Datas>
</Root>
In order to generate the {http://www.w3.org/2001/XMLSchema-instance}type attribute using XmlSerializer, you need to attach [XmlInclude(typeof(XXX))] for all subclasses XXX of Data to a declared type somewhere in your object graph, i.e. on the Root class or the Data class itself:
//[XmlInclude(typeof(DataA))] /* Could also go here if you prefer. */
//[XmlInclude(typeof(DataB))] /* Could also go here if you prefer. */
public class Data
{
[XmlElement("Name")]
public string Name { get; set; }
}
public class DataA : Data
{
[XmlElement("ADesc")]
public string ADesc { get; set; }
}
public class DataB : Data
{
[XmlElement("BDesc")]
public string BDesc { get; set; }
}
[XmlRoot("Root")]
[XmlInclude(typeof(DataA))]
[XmlInclude(typeof(DataB))]
public class Root
{
[XmlArray("Datas")]
public List<Data> Datas { get; set; }
}
For more information, see Declaring Serialization Types in Troubleshooting Common Problems with the XmlSerializer and also Xsi:type Attribute Binding Support.
I want to create a form that is comprised of fields that come from separate models and then, when submitted, updates the corresponding tables in the database.
I couldn't find the answer anywhere so I tinkered with things until I made something work. Being new to all of this, however, I doubt the efficacy of my solution. I'm hoping that by stating my problem and showing my solution you can better understand what I need and be able to help me find the best solution for my problem.
I like simple, abstract examples so here is what I currently have working:
First, I make a Color Model...
namespace example.Models
{
public class Color
{
public int ColorID { get; set; }
public string Name { get; set; }
}
}
Then I make a Shape Model...
namespace example.Models
{
public class Shape
{
public int ShapeID { get; set; }
public string Name { get; set; }
}
}
Next, I make a simple ViewModel...
namespace example.ViewModels
{
public class ColorAndShapeViewModel
{
public Color Color { get; set; }
public Shape Shape { get; set; }
}
}
Here is my DbContext...
namespace example.DAL
{
public class MyContext : DbContext
{
public DbSet<Color> Colors { get; set; }
public DbSet<Shape> Shapes { get; set; }
}
}
Next, the Controller...
namespace example.Controllers
{
public class MyController : Controller
{
private MyContext db = new MyContext();
public ActionResult Index()
{
return View();
}
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ColorAndShapeViewModel viewmodel)
{
Color newColor = new Color();
newColor.Name = viewmodel.Color.Name;
Shape newShape = new Shape();
newShape.Name = viewmodel.Shape.Name;
db.Colors.Add(newColor);
db.Shapes.Add(newShape);
db.SaveChanges();
return RedirectToAction("Index");
}
}
}
Lastly, the View...
#model example.ViewModels.ColorAndShapeViewModel
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.LabelFor(model => model.Color.Name)<br />
#Html.TextBoxFor(model => model.Color.Name)<br />
<br />
#Html.LabelFor(model => model.Shape.Name)<br />
#Html.TextBoxFor(model => model.Shape.Name)<br />
<br />
<input type="submit" value="Create" />
}
Thus far, everything works as expected: I am able to enter data for separate models into a single form and submit it. The database is updated (the color goes into the color table, the shape into the shape table) and all is well—or is it? In reality, I will have a much longer form using several models which are related to each other. My present solution would require a large controller, full of newObject.Property = viewModel.Object.Property statements. Isn't that undesirable? Is there a better way to handle this?
Thanks!
I'm faced with a confusing problem where in my Edit or Create action result methods, EF4 will throw a DbEntityValidationException with the inner message stating:
The field Body must be a string or
array type with a maximum length of
'128'.
The model in question looks like this:
[Table("tblArticles")]
public class Article
{
[Key]
public int ID { get; set; }
[Required(ErrorMessage="Title must be included")]
public string Title { get; set; }
[AllowHtml]
public string Body { get; set; }
[Required(ErrorMessage="Start Date must be specified")]
[Display(Name="Start Date")]
[DisplayFormat(DataFormatString="dd-mm-yyyy")]
public DateTime? StartDate { get; set; }
[Required(ErrorMessage = "End Date must be specified")]
[Display(Name = "End Date")]
public DateTime? EndDate { get; set; }
public int Priority { get; set; }
public bool Archived { get; set; }
public virtual ICollection<ArticleImage> Images { get; set; }
}
The "Body" field in the actual database is of type Text, so there's no obvious limit there. The data that I'm trying to post is this:
<p>
This is an example to confirm that new articles are looking right.</p>
<p>
<img alt="" src="http://www.google.co.nz/logos/2011/houdini11-sr.jpg"
style="width: 160px; height: 56px; float: left;" /></p>
An example of the Edit method looks like this:
[HttpPost]
public ActionResult Edit(Article article)
{
if (ModelState.IsValid)
{
try
{
articleRepository.Update(article);
}
catch (DbEntityValidationException dbevEx)
{
ErrorSignal.FromCurrentContext().Raise(dbevEx);
ModelState.AddModelError("FORM", dbevEx);
return View("Edit", article);
}
// Other exception handling happens...
}
return RedirectToAction("Index");
}
And finally, the method that actually does the grunt work is:
public void Update(T Entity)
{
dbset.Attach(Entity);
db.Entry(Entity).State = System.Data.EntityState.Modified;
db.Commit();
}
I can't see anything in code or in the database that might be causing the problem, so where else should I look?
Default length of string field in code first is 128. If you are using EF validation it will throw exception. You can extend the size by using:
[StringLength(Int32.MaxValue)]
public string Body { get; set; }
This post became somehow popular so I'm adding second approach which also works:
[MaxLength]
public string Body { get; set; }
StringLengthAttribute is from System.ComponentModel.DataAnnotations assembly and MaxLengthAttribute is from EntityFramework assembly (EF 4.1).
If you get this error using a Model First approach, check the EF Model: it may be simply that a property on the Entity you're updating has the Max Length attribute set.
For Entity Framework 4.3.1, 5.0.0 and 6.2.0, you can use IsMaxLength().
Property(m => m.Body ).IsRequired().IsMaxLength();
Configures the column to allow the maximum length supported by the database provider.
https://learn.microsoft.com/en-us/dotnet/api/system.data.entity.modelconfiguration.configuration.stringcolumnconfiguration.ismaxlength?view=entity-framework-6.2.0
May be you have used
Property(m => m.Body ).IsRequired().HasMaxLength(128);
On your dbcontext class in OnModelCreating.
So change as per your length
For me, [MaxLength] didn't work. I have added below statement to the context.
modelBuilder.Entity<YourModel>().Property(e => e.YourColumn).HasMaxLength(4000);
I have not tried exceeding "4000" limit but I think it can be extended further. You don't have to keep it 128 as the error showed by compiler.