Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data will not deserialize out of XML files for ASP.NET Core #2130

Open
poppastring opened this issue Aug 4, 2019 · 3 comments

Comments

@poppastring
Copy link

commented Aug 4, 2019

Describe the bug

I am attempting to pull data from a configuration file during the start up of my ASP.NET Core app. I am attempting to deserialize the data straight to a class. This works for regular properties but not when I need to pull an array of data.

Update: I have recreated the whole problem in a smaller project that you can download from OneDrive here. It shows a working example (with type section) next to a failing example (type validcommenttags).

To Reproduce

Steps to reproduce the behavior:

configBuilder.AddXmlFile("some xml file reference")
...
services.Configure(Configuration);

I am using a configuration file roughly formatted as follows (see here):

<SiteConfig>
    ...
   <validCommentTags>
      <tag name="a" attributes="href,title" allowed="true" />
      <tag name="b" attributes="" allowed="true" />
   </validCommentTags>
    ...
</SiteConfig>

An example of the following code is here:

public interface ISiteConfig
{
    ...
    [XmlArray("validCommentTags", IsNullable = true)]
    [XmlArrayItem("tag", Type = typeof(ValidTag))]
    ValidTag[] ValidCommentTagArray { get; set; }
    ...
}

Now the data appears to be pulled from the file I see the following in configBuilder.Build() - Microsoft.Extensions.Configuration.ConfigurationRoot.

   {[validCommentTags:tag:a:name, a]}
   {[validCommentTags:tag:a:attributes, href,title]}
   {[validCommentTags:tag:a:allowed, true]}
   {[validCommentTags:tag:b:name, b]}
   {[validCommentTags:tag:b:attributes, ]}
   {[validCommentTags:tag:b:allowed, true]}
  1. I am using netcoreapp2.1
  2. In fact you can download and run the app where I am trying this from here.

Expected behavior

I am expecting to be able pull in my xml data to to an array, collection or list of objects.

cc @anurse

@anurse

This comment has been minimized.

Copy link
Member

commented Aug 5, 2019

I think you're hitting a limitation of the config system here. The main issue is that the config system is designed around a list of hierarchical key-value pairs (with grandparent:parent:child syntax describing the "hierarchy"). Config providers, like the XML provider, load data into that format and then the config binding system can bind that to objects. The config providers don't know anything about the object you're going to bind to, they just load key-value pairs. As a result, XML serialization attributes like XmlArray don't apply.

In order to support arrays, the config provider has to use numeric keys when loading the values into the key-value pairs, for example validCommentTags:tag:0:name, validCommentTags:tag:1:name, etc. It looks like the XML provider does not do this. The XML provider basically just builds a hierarchical name by concatenating all the parent element names together (and the attribute name, if the value is in an attribute). It would be a breaking change to introduce array support and it's also not entirely clear how it would work (perhaps a series of same-named elements would be treated as an array?)

One thing that the XML provider does to is treat the name attribute as an override for the tag name. I'm a little surprised that you're seeing the XML you specified return a key like this: validCommentTags:tag:a:attributes since by my reading of the XML config provider it should be validCommentTags:a:attributes (note that the tag name was overridden by name, not added as an additional prefix).

Are the name values guaranteed to be unique? If so, you could consider binding to a dictionary. Binding the config keys you're seeing to an object structure like this should work in that case:

public interface ISiteConfig
{
    ...
    ValidCommentTags ValidCommentTags { get; set; }
    ...
}

public class ValidCommentTags
{
    IDictionary<string, ValidCommentTag> Tag { get; set; }
}

public class ValidCommentTag
{
    string Name { get; set; }
    ...
}

Since you are working on a port of an existing application to .NET Core, is the goal here compatbility with the existing XML configuration files for dasBlog? In that case I do think that a custom config provider might be appropriate here. You could still take advantage of the config system, but you'd add a custom SiteConfigConfigurationProvider (implementing IConfigurationSource, I'd suggest using FileConfigurationSource and FileConfigurationProvider as base classes though since they have a more appropriate API for file-based providers). In that provider you can parse the XML files however you want and adapt them to fit what the config system needs. For example, you could take well-known arrays like validCommentTags and properly generate key-value pairs like: validCommentTags:0:name=a.

Alternatively I think it would be reasonable to not use the configuration system at all and continue using direct XML deserialization logic.

@anurse anurse added this to the Discussions milestone Aug 5, 2019

@poppastring

This comment has been minimized.

Copy link
Author

commented Aug 5, 2019

"Are the name values guaranteed to be unique? If so, you could consider binding to a dictionary."

Yes, they are. The Dictionary option would be the most straightforward resolution but I am still unable to get that to work to be honest.

"Since you are working on a port of an existing application to .NET Core, is the goal here compatbility with the existing XML configuration files for dasBlog?"

It started that way but I am at the point where I am considering a port to JSON config files if this does not work. It is not as necessary because many of the config settings are obsolete in the dasblog-core. The compatibility with the old dasBlog system would be be focused only on the blog posts in XML format (we use another mechanism to read those).

@poppastring

This comment has been minimized.

Copy link
Author

commented Aug 5, 2019

Update: I got List<ValidTag> to work in my test project I should be good to go now! Many thanks!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.