Portable.Xaml icon indicating copy to clipboard operation
Portable.Xaml copied to clipboard

fix prefix dupplication error

Open whistyun opened this issue 4 years ago • 7 comments

If there are some namespaces of the same acronym, XamlServices.Save cause XmlException with the message "'xmlns:p' is a duplicate attribute name".

Demonstration code

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            var list = new List<object>();
            list.Add(new Charlie());
            list.Add(new Carol()); // if this line comment out, no error occured

            var txt = Portable.Xaml.XamlServices.Save(list);
            Console.WriteLine(txt);
        }
    }
}

namespace Alpha.Bravo
{
    public class Charlie
    {
        public Chocolate FavoriteFood { set; get; } = new Chocolate();
    }
}

namespace Alice.Bob
{
    public class Carol
    {
        public Chocolate FavoriteFood { set; get; } = new Chocolate();
    }
}

namespace Apple.Banana
{
    public class Chocolate
    {
    }
}


This pull request avoids the above problem by adding an ordinal number at the end

whistyun avatar Dec 21 '20 10:12 whistyun

I think this build failure is bacause of it. Should I modify yml file for workaround?

whistyun avatar Dec 21 '20 10:12 whistyun

Thanks for submitting the PR! This is great.

There's a few things.. would you be able to write it without having a capturing lambda or enumerable (foreach)? Also, a test would be great to have to ensure this doesn't break in the future.

cwensley avatar Dec 21 '20 15:12 cwensley

.. as for the build error, yes if you know what is needed to be done to fix It please update. I'll probably update this to use .NET 5 soon anyway.

cwensley avatar Dec 21 '20 15:12 cwensley

would you be able to write it without having a capturing lambda or enumerable (foreach)?

I can't come up with a way to write without 'Any' method nor enumerable. I'm going to avoid call 'Any' in while loop. However don't you mean it?

public string AddNamespace (string ns)
{
	var l = Namespaces;
	string prefix, s;

	...

	else
	{
		s = sctx.GetPreferredPrefix(ns);
		if (!l.Any(i => i.Prefix == s))
			prefix = s;
		else 
		{
			int getSuffix(string p)
				=> !p.StartsWith("p") ? 0 :
				   int.TryParse(p.Substring(1), out var i) ? i :
				   1;

			var idx = l.Max(i => getSuffix(i.Prefix)) + 1;
			prefix = s + idx;
		}
	}

	...
}

whistyun avatar Dec 22 '20 03:12 whistyun

How about creating a function:

		bool AnyHavePrefix(List<NamespaceDeclaration> namespaces, string prefix)
		{
			for (int i = 0; i < namespaces.Count; i++)
			{
				var ns = namespaces[i];
				if (ns.Prefix == prefix)
					return true;
			}
			return false;
		}

After looking into that method, I realize there's another use of an Any() with capturing lambda in that method too... There's no reason we couldn't make it more optimized while we notice it, I hope you don't mind.

Also, just a quick question.. why do you start at 2? Does System.Xaml behave this way?

cwensley avatar Dec 22 '20 03:12 cwensley

When I checked System.Xaml, it started from 1. I will match it with System.Xaml.

By the way, System.Xaml uses the acronyms, not the ‘p’ (for example, the demo code uses ‘ab’, ‘ab1’, ‘ab2’).

~~If possible, if GetPreferredPrefix returns 'p', I will use the acronyms instead.~~ If we can get the acronyms, I will use the acronyms and when prefix duplication is occurred, add suffix to the acronyms. If we can't get the acronym, use GetPreferredPrefix as an alternative.

whistyun avatar Dec 22 '20 08:12 whistyun

@cwensley I fixed to start from 1. and If a acronyms is gotten, it 's used. I Also added some test cases.

Additionary, I added workaround build.yml to avoid build failure.

whistyun avatar Dec 23 '20 23:12 whistyun