루트 요소에 기본 네임 스페이스를 쓰지 않도록 XmlSerializer를 구성하는 방법이 있습니까?
내가 얻는 것은 이것입니다 :
<?xml ...>
<rootelement xmlns:xsi="..." xmlns:xsd="...">
</rootelement>
xmlns 선언을 모두 제거하고 싶습니다.
루트 요소에 기본 네임 스페이스를 쓰지 않도록 XmlSerializer를 구성하는 방법이 있습니까?
내가 얻는 것은 이것입니다 :
<?xml ...>
<rootelement xmlns:xsi="..." xmlns:xsd="...">
</rootelement>
xmlns 선언을 모두 제거하고 싶습니다.
답변:
Dave가 .NET 에서 객체를 직렬화 할 때 모든 xsi 및 xsd 네임 스페이스 생략에 대한 답변을 반복하도록 요청 했으므로이 게시물을 업데이트하고 위에서 언급 한 링크에서 답변을 반복했습니다. 이 답변에 사용 된 예는 다른 질문에 사용 된 예와 동일합니다. 다음 내용이 그대로 복사됩니다.
Microsoft의 설명서와 여러 가지 솔루션을 온라인에서 읽은 후이 문제에 대한 해결책을 찾았습니다. 를 통해 내장 XmlSerializer
및 사용자 정의 XML 직렬화와 함께 작동합니다 IXmlSerialiazble
.
간단히 말해서, 지금 MyTypeWithNamespaces
까지이 질문에 대한 답변에 사용 된 것과 동일한 XML 샘플을 사용하겠습니다.
[XmlRoot("MyTypeWithNamespaces", Namespace="urn:Abracadabra", IsNullable=false)]
public class MyTypeWithNamespaces
{
// As noted below, per Microsoft's documentation, if the class exposes a public
// member of type XmlSerializerNamespaces decorated with the
// XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
// namespaces during serialization.
public MyTypeWithNamespaces( )
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
// Don't do this!! Microsoft's documentation explicitly says it's not supported.
// It doesn't throw any exceptions, but in my testing, it didn't always work.
// new XmlQualifiedName(string.Empty, string.Empty), // And don't do this:
// new XmlQualifiedName("", "")
// DO THIS:
new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
// Add any other namespaces, with prefixes, here.
});
}
// If you have other constructors, make sure to call the default constructor.
public MyTypeWithNamespaces(string label, int epoch) : this( )
{
this._label = label;
this._epoch = epoch;
}
// An element with a declared namespace different than the namespace
// of the enclosing type.
[XmlElement(Namespace="urn:Whoohoo")]
public string Label
{
get { return this._label; }
set { this._label = value; }
}
private string _label;
// An element whose tag will be the same name as the property name.
// Also, this element will inherit the namespace of the enclosing type.
public int Epoch
{
get { return this._epoch; }
set { this._epoch = value; }
}
private int _epoch;
// Per Microsoft's documentation, you can add some public member that
// returns a XmlSerializerNamespaces object. They use a public field,
// but that's sloppy. So I'll use a private backed-field with a public
// getter property. Also, per the documentation, for this to work with
// the XmlSerializer, decorate it with the XmlNamespaceDeclarations
// attribute.
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces
{
get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;
}
이것이이 수업의 전부입니다. 이제 일부 XmlSerializerNamespaces
는 자신의 클래스 내에 객체 를 두는 것에 반대했습니다 . 그러나 당신이 볼 수 있듯이, 나는 기본 생성자에 깔끔하게 자리 잡고 네임 스페이스를 반환하는 공용 속성을 노출했습니다.
이제 클래스를 직렬화 할 때 다음 코드를 사용합니다.
MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);
/******
OK, I just figured I could do this to make the code shorter, so I commented out the
below and replaced it with what follows:
// You have to use this constructor in order for the root element to have the right namespaces.
// If you need to do custom serialization of inner objects, you can use a shortened constructor.
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces), new XmlAttributeOverrides(),
new Type[]{}, new XmlRootAttribute("MyTypeWithNamespaces"), "urn:Abracadabra");
******/
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });
// I'll use a MemoryStream as my backing store.
MemoryStream ms = new MemoryStream();
// This is extra! If you want to change the settings for the XmlSerializer, you have to create
// a separate XmlWriterSettings object and use the XmlTextWriter.Create(...) factory method.
// So, in this case, I want to omit the XML declaration.
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Encoding = Encoding.UTF8; // This is probably the default
// You could use the XmlWriterSetting to set indenting and new line options, but the
// XmlTextWriter class has a much easier method to accomplish that.
// The factory method returns a XmlWriter, not a XmlTextWriter, so cast it.
XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);
// Then we can set our indenting options (this is, of course, optional).
xtw.Formatting = Formatting.Indented;
// Now serialize our object.
xs.Serialize(xtw, myType, myType.Namespaces);
이 작업을 완료하면 다음과 같은 결과가 나타납니다.
<MyTypeWithNamespaces>
<Label xmlns="urn:Whoohoo">myLabel</Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
웹 서비스 호출을 위해 XML로 직렬화되는 클래스가 심층적 인 최근 프로젝트 에서이 방법을 성공적으로 사용했습니다. Microsoft의 문서는 공개적으로 접근 가능한 방법으로 수행 할 작업에 대해 명확하지 않습니다.XmlSerializerNamespaces
일단 회원을 만든 후에 , 많은 사람들이 쓸모 없다고 생각합니다. 그러나 설명서를 따라 위에서 설명한 방식으로 사용하면 지원되지 않는 동작이나 "자신의 롤링"직렬화를 구현하지 않고 XmlSerializer가 클래스에 대해 XML을 생성하는 방법을 사용자 지정할 수 있습니다 IXmlSerializable
.
이 답변이 모든 표준에 의해 생성 된 표준 xsi
및 xsd
네임 스페이스를 제거하는 방법을 한 번에 쉬게하기를 희망합니다 .XmlSerializer
.
업데이트 : 모든 네임 스페이스를 제거하는 것에 대한 OP의 질문에 대답하고 싶습니다. 위의 코드는이 작업을 수행합니다. 방법을 보여 드리겠습니다. 위의 예에서 사용중인 네임 스페이스가 두 개이므로 모든 네임 스페이스를 제거 할 수 없습니다. XML 문서 어딘가에와 같은 것이 필요합니다 xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo
. 예제에서 클래스가 큰 문서의 일부인 경우, 어딘가 네임 스페이스 위 중의 하나 (또는 둘 다)에 대해 선언해야 Abracadbra
하고 Whoohoo
. 그렇지 않은 경우 네임 스페이스 중 하나 또는 둘 다에있는 요소를 일종의 접두어로 장식해야합니다 (두 개의 기본 네임 스페이스를 가질 수 없습니까?). 따라서이 예에서는 Abracadabra
기본 네임 스페이스입니다. MyTypeWithNamespaces
클래스 내 에서 네임 스페이스의 네임 스페이스 접두사를 다음 Whoohoo
과 같이 추가 할 수 있습니다 .
public MyTypeWithNamespaces
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:Abracadabra"), // Default Namespace
new XmlQualifiedName("w", "urn:Whoohoo")
});
}
이제 클래스 정의에서 <Label/>
요소가 namespace에 있음을 나타내 "urn:Whoohoo"
므로 더 이상 아무것도 할 필요가 없습니다. 위의 직렬화 코드를 변경하지 않고 클래스를 직렬화하면 다음과 같이 출력됩니다.
<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
<w:Label>myLabel</w:Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
<Label>
문서의 나머지 부분과 다른 네임 스페이스에 있기 때문에 네임 스페이스를 사용하여 "장식"해야합니다. 아무 여전히 있다는 것을 공지 사항 xsi
및 xsd
네임 스페이스.
이것으로 다른 질문에 대한 대답이 끝납니다. 그러나 네임 스페이스를 사용하지 않는 것에 대한 OP의 질문에 아직 답변하지 않았다고 생각하기 때문에 OP를 원했습니다. <Label>
이것이 문서의 나머지 부분과 동일한 네임 스페이스의 일부 라고 가정하십시오 urn:Abracadabra
.
<MyTypeWithNamespaces>
<Label>myLabel<Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
생성자는 기본 코드 이름을 검색하기 위해 public 속성과 함께 첫 번째 코드 예제에서와 같이 보입니다.
// As noted below, per Microsoft's documentation, if the class exposes a public
// member of type XmlSerializerNamespaces decorated with the
// XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
// namespaces during serialization.
public MyTypeWithNamespaces( )
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
});
}
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces
{
get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;
그런 다음 나중에 MyTypeWithNamespaces
객체를 사용하여 직렬화하는 코드에서 위와 같이 호출합니다.
MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });
...
// Above, you'd setup your XmlTextWriter.
// Now serialize our object.
xs.Serialize(xtw, myType, myType.Namespaces);
그리고 XmlSerializer
출력에 추가 네임 스페이스가없는 바로 위에 표시된 것과 동일한 XML을 뱉어냅니다.
<MyTypeWithNamespaces>
<Label>myLabel<Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);
로 대체해야했습니다 var xtw = XmlTextWriter.Create(memStm, xws);
.
XmlTextWriter.Create
(추상?) XmlWriter
인스턴스를 반환 합니다. 따라서 @ Preza8이 정확하므로 다른 XmlTextWriter
특정 속성을 (다운 캐스팅하지 않고는) 설정 할 수 없으므로 특정 캐스트는로 캐스팅됩니다 XmlTextWriter
.
//Create our own namespaces for the output
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
//Add an empty namespace and empty value
ns.Add("", "");
//Create the serializer
XmlSerializer slz = new XmlSerializer(someType);
//Serialize the object with our own namespaces (notice the overload)
slz.Serialize(myXmlTextWriter, someObject, ns)
대안이 있습니다 . 직렬화 할 형식에 XmlSerializerNamespaces 형식의 멤버를 제공 할 수 있습니다 . XmlNamespaceDeclarations로 장식하십시오. 속성으로 . 네임 스페이스 접두사와 URI를 해당 멤버에 추가하십시오. 그런 다음 XmlSerializerNamespaces를 명시 적으로 제공하지 않는 직렬화는 사용자가 입력 한 네임 스페이스 접두사 + URI 쌍을 사용합니다.
예제 코드, 이것이 당신의 유형이라고 가정하십시오 :
[XmlRoot(Namespace = "urn:mycompany.2009")]
public class Person {
[XmlAttribute]
public bool Known;
[XmlElement]
public string Name;
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces xmlns;
}
당신은 이것을 할 수 있습니다 :
var p = new Person
{
Name = "Charley",
Known = false,
xmlns = new XmlSerializerNamespaces()
}
p.xmlns.Add("",""); // default namespace is emoty
p.xmlns.Add("c", "urn:mycompany.2009");
즉, 고유 한 접두사 + URI 쌍을 지정하지 않은 인스턴스의 직렬화는 "urn : mycompany.2009"네임 스페이스에 "p"접두사를 사용합니다. 또한 xsi 및 xsd 네임 스페이스도 생략합니다.
차이점은 XmlSerializer.Serialize ()를 호출 할 때 XmlSerializerNamespaces를 명시 적으로 사용하지 않고 형식 자체에 추가한다는 것입니다. 즉, 소유하지 않은 코드 (예 : 웹 서비스 스택)로 유형의 인스턴스가 직렬화되고 해당 코드가 XmlSerializerNamespaces를 명시 적으로 제공하지 않으면 해당 직렬 변환기는 인스턴스에 제공된 네임 스페이스를 사용합니다.
나는 사용하고있다 :
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
class Program
{
static void Main(string[] args)
{
const string DEFAULT_NAMESPACE = "http://www.something.org/schema";
var serializer = new XmlSerializer(typeof(Person), DEFAULT_NAMESPACE);
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("", DEFAULT_NAMESPACE);
using (var stream = new MemoryStream())
{
var someone = new Person
{
FirstName = "Donald",
LastName = "Duck"
};
serializer.Serialize(stream, someone, namespaces);
stream.Position = 0;
using (var reader = new StreamReader(stream))
{
Console.WriteLine(reader.ReadToEnd());
}
}
}
}
다음 XML을 얻으려면
<?xml version="1.0"?>
<Person xmlns="http://www.something.org/schema">
<FirstName>Donald</FirstName>
<LastName>Duck</LastName>
</Person>
네임 스페이스를 원하지 않으면 DEFAULT_NAMESPACE를 ""로 설정하십시오.