성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
[공진영] 안녕하세요 좋은글 감사합니다. 현재 제가 wpf로 관제 모...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - Microsoft.XmlSerializer.Generator 처리 없이 XmlSerializer 생성자를 예외 없이 사용하고 싶다면?</h1> <p> 지난 글을 통해,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .NET Core/5+를 위한 SGen(Microsoft.XmlSerializer.Generator) 사용법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13196'>https://www.sysnet.pe.kr/2/0/13196</a> .NET Core/5+ 환경에서 XmlSerializer 사용 시 System.IO.FileNotFoundException 예외 발생하는 경우 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13197'>https://www.sysnet.pe.kr/2/0/13197</a> </pre> <br /> XmlSerializer와 그것의 "[어셈블리명].XmlSerializers.dll" 생성을 알아봤는데요, 사실 저렇게 처리하는 것이 정석이긴 합니다. 하지만, 때로는 부가적인 DLL을 함께 배포한다는 것이 좀 귀찮을 수도 있습니다.<br /> <br /> 혹시 dll을 생성하지 않으면서도 FileNotFoundException를 비껴갈 해법이 있을까요?<br /> <br /> 우선, 가장 간단한 방법은 Microsoft.XmlSerializer.Generator 패키지로 생성된 dll을 역어셈블해 해당 코드를 그대로 긁어다 자신의 프로젝트에 포함하는 것입니다. 하지만, 듣기만 해도 너무 원시적이죠? ^^<br /> <br /> 다행히 그보다 더 우아한 방법이 있습니다. ^^ 해당 패키지를 추가한 후 빌드하면 다음과 같은 메시지가 보이는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Rebuild started... 1>------ Rebuild All started: Project: ConsoleApp1, Configuration: Debug Any CPU ------ Restored C:\temp\ConsoleApp1\ConsoleApp1\ConsoleApp1.csproj (in 1 ms). Restored C:\temp\ConsoleApp1\ConsoleApp1\ConsoleApp1.csproj (in 1 ms). 1>ConsoleApp1 -> C:\temp\ConsoleApp1\ConsoleApp1\bin\Debug\net7.0\ConsoleApp1.dll 1>.NET Xml Serialization Generation Utility, Version 7.0.0] 1>Serialization Code File Name: <span style='color: blue; font-weight: bold'>C:\temp\ConsoleApp1\ConsoleApp1\obj\Debug\net7.0\ConsoleApp1.XmlSerializers.cs</span>. 1>Generated serialization code for assembly C:\temp\ConsoleApp1\ConsoleApp1\obj\Debug\net7.0\ConsoleApp1.dll --> 'C:\temp\ConsoleApp1\ConsoleApp1\obj\Debug\net7.0\ConsoleApp1.XmlSerializers.cs'. ========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ========== ========== Elapsed 00:01.222 ========== </pre> <br /> 따라서 저렇게 생성된 ConsoleApp1.XmlSerializers.cs 파일을 가져와서 현재의 프로젝트에 재사용할 수 있습니다. 그렇게 추가하면 당연히 Microsoft.XmlSerializer.Generator와 충돌이 발생하기 때문에 한번 생성한 이후에는 PackageReference를 주석 처리해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <span style='color: blue; font-weight: bold'><!-- PackageReference Include="Microsoft.XmlSerializer.Generator" Version="7.0.0" / --></span> </ItemGroup> </Project> </pre> <br /> 하지만 이렇게 바꾸고 나면 이전의 System.IO.FileNotFoundException 예외가 다시 발생하게 됩니다. 왜냐하면 해당 Serializer가 현재의 어셈블리에 있다는 것을 알지 못하므로 마찬가지로 DLL 로딩을 시도하기 때문입니다.<br /> <br /> 이것을 해결하기 위해서는 XmlSerializer의 대상이 되는 타입에,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > XmlSerializer xs = new XmlSerializer(typeof(<span style='color: blue; font-weight: bold'>MyType</span>)); </pre> <br /> 다음과 같이 XmlSerializerAssemblyAttribute 특성을 지정하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>[XmlSerializerAssemblyAttribute()]</span> public class MyType { public int Age; } </pre> <br /> 특성의 인자로 Assembly를 지정할 수 있는데, 기본값 null인 경우에는 현재 어셈블리 내에서 찾기 때문에 정상적으로 우리가 추가했던 "ConsoleApp1.XmlSerializers.cs" 파일의 코드들이 로드돼 실행되는 것을 확인할 수 있습니다.<br /> <br /> (첨부 파일은 이 글의 예제 코드를 포함합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, 때로는 Microsoft.XmlSerializer.Generator 패키지가 자동 생성한 cs 코드에 너무 잡다한 타입들이 많이 정의될 수도 있습니다. 그런 것이 마음에 들지 않는다면, 어쩔 수 없습니다. ^^ 좀 불편하더라도 Dummy 프로젝트를 하나 만들고 XML 직렬화에 사용되는 타입이 정의된 파일들만 Link 기능으로 포함시켜 빌드하는 식으로 대응할 수 있습니다.<br /> <br /> 또는 만약 XML 대상 타입이 직접 직렬화를 제어하도록 <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.xml.serialization.ixmlserializable'>IXmlSerializable</a>을 구현한 경우라면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > XmlSerializer와 Dictionary 타입 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12942#dict_xml'>https://www.sysnet.pe.kr/2/0/12942#dict_xml</a> </pre> <br /> 어차피 Microsoft.XmlSerializer.Generator 패키지에서 제공할 직렬화 관련 코드도 필요 없기 때문에 그냥 단순하게 다음과 같은 빈 XmlSerializerContract를 포함시켜도 무방합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > namespace Microsoft.Xml.Serialization.GeneratedAssembly { public class XmlSerializerContract : <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlserializerimplementation'>XmlSerializerImplementation</a> { public XmlSerializerContract() { } public override bool CanSerialize(Type type) { return false; } public override XmlSerializationReader Reader => null; public override XmlSerializationWriter Writer => null; public override XmlSerializer GetSerializer(Type type) { return null; } public override Hashtable ReadMethods => null; public override Hashtable WriteMethods => null; public override Hashtable TypedSerializers => null; } } </pre> <br /> 사실, 위의 코드는 모든 타입을 위해 사용할 수 있습니다. CanSerialize가 false를 반환하고 있는데요, XmlSerializer 생성자에서는 저렇게 된 경우 메모리에 직렬화 관련 어셈블리를 동적으로 생성합니다.<br /> <br /> 따라서, 이거저거 귀찮으신 분은 그냥 단순하게 여러분의 프로젝트에 빈 구현을 포함하고 있는 위의 XmlSerializerContract 타입 정의를 포함시키고 여러분의 모든 XML 직렬화 타입에 XmlSerializerAssemblyAttribute만을 적용해 두면 됩니다. 그럼, System.IO.FileNotFoundException 발생 없이 동적으로 생성된 직렬화의 도움을 자연스럽게 받을 수 있습니다.<br /> <br /> 즉, (Microsoft.XmlSerializer.Generator 사용도 필요 없고 단순히) 아래와 같이 작성해 주면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Collections; using System.Reflection; using System.Xml.Serialization; internal class Program { static void Main(string[] args) { XmlSerializer xs = new XmlSerializer(typeof(MyType)); XmlSerializer xs = new XmlSerializer(typeof(MyType2)); } } [XmlSerializerAssemblyAttribute()] public class MyType { public int Age; } [XmlSerializerAssemblyAttribute()] public class MyType2 { public int Age; } namespace Microsoft.Xml.Serialization.GeneratedAssembly { public class XmlSerializerContract : XmlSerializerImplementation { public XmlSerializerContract() { } public override bool CanSerialize(Type type) { return false; } public override XmlSerializationReader Reader => null; public override XmlSerializationWriter Writer => null; public override XmlSerializer GetSerializer(Type type) { return null; } public override Hashtable ReadMethods => null; public override Hashtable WriteMethods => null; public override Hashtable TypedSerializers => null; } } </pre> <br /> 실행하면, FileNotFoundException은 없고 Debug Output 창에는 "'ConsoleApp2.exe' (CoreCLR: clrhost): Loaded 'Microsoft.GeneratedCode'."라는 메시지를 통해 동적 생성된 어셈블리가 사용되었음을 알 수 있는 메시지만 확인됩니다.<br /> <br /> 물론, 실행 시마다 직렬화 관련 코드를 동적으로 생성해 어셈블리를 만드는 부하가 있다는 단점은 있습니다. 만약 그런 부하조차 부담스럽다면 Microsoft.XmlSerializer.Generator 패키지가 만들어 준 cs 파일을 포함하면 되는데요, 하지만, 저는 그냥 동적 생성하는 것을 권하고 싶습니다. 왜냐하면, Generator 패키지를 경유하도록 했다면 만약 xml 직렬화 타입을 변경한 경우 반드시 cs 파일을 재생성해야 하는 것을 잊어서는 안 되기 때문입니다. 그런 과정을 자동화하지 않고 수동으로 하는 경우 자칫 그 절차를 모르고 지나간다면 쓸데없는 버그에 휘말릴 수 있습니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1997&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p> <br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1692
(왼쪽의 숫자를 입력해야 합니다.)