객체를 XML로 직렬화


292

상속받은 C # 클래스가 있습니다. 개체를 성공적으로 "빌드"했습니다. 그러나 객체를 XML로 직렬화해야합니다. 쉬운 방법이 있습니까?

클래스가 직렬화를 위해 설정된 것처럼 보이지만 XML 표현을 얻는 방법을 잘 모르겠습니다. 내 수업 정의는 다음과 같습니다.

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.domain.com/test")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://www.domain.com/test", IsNullable = false)]
public partial class MyObject
{
  ...
}

여기 내가 할 수 있다고 생각했지만 작동하지 않습니다.

MyObject o = new MyObject();
// Set o properties
string xml = o.ToString();

이 객체의 XML 표현을 어떻게 얻습니까?



1
나는 이것을 달성하기 위해 간단한 라이브러리를 개발했다 : github.com/aishwaryashiva/SaveXML
Aishwarya Shiva

답변:


510

XML 직렬화에는 XmlSerializer를 사용해야합니다. 아래는 샘플 스 니펫입니다.

 XmlSerializer xsSubmit = new XmlSerializer(typeof(MyObject));
 var subReq = new MyObject();
 var xml = "";

 using(var sww = new StringWriter())
 {
     using(XmlWriter writer = XmlWriter.Create(sww))
     {
         xsSubmit.Serialize(writer, subReq);
         xml = sww.ToString(); // Your XML
     }
 }

10
라인없이 완벽하게 작동하는 것 같습니다XmlWriter writer = XmlWriter.Create(sww);
Paul Hunt

15
직렬화 된 객체를 포맷하려면 : XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented };대신XmlWriter writer = XmlWriter.Create(sww);
Tono Nam

4
XmlWriter캡슐화하기 때문에 StringWriter두 가지를 모두 처리 할 필요가 없습니다 (처음 사용하는 것이 중복 임), 맞습니까? 나는 그것을 XmlWriter처리 한다고 가정 합니다 ...
talles

4
@talles XmlWriter는을 캡슐화하지 않고 StringWriter전달 된 것을 활용 StringWriter하고 처리 할 것으로 예상 / 책임이 없습니다. 또한 범위를 StringWriter벗어난 XmlWriter경우 XmlWriter, 폐기 시 여전히 원할 수 XmlWriter있습니다 StringWriter. 일반적으로 처분이 필요한 것을 선언하면 처분 할 책임이 있습니다. 그리고 그 규칙에 암시 적으로, 당신이 스스로 선언하지 않은 것은 처분해서는 안됩니다. 따라서 두 가지가 모두 using필요합니다.
Arkaine55

3
System.Xml.Serialization 사용; System.IO 사용; System.Xml 사용;
timothy

122

아래처럼 ref 변수를 사용하는 대신 문자열을 반환하도록 내 것을 수정했습니다.

public static string Serialize<T>(this T value)
{
    if (value == null)
    {
        return string.Empty;
    }
    try
    {
        var xmlserializer = new XmlSerializer(typeof(T));
        var stringWriter = new StringWriter();
        using (var writer = XmlWriter.Create(stringWriter))
        {
            xmlserializer.Serialize(writer, value);
            return stringWriter.ToString();
        }
    }
    catch (Exception ex)
    {
        throw new Exception("An error occurred", ex);
    }
}

사용법은 다음과 같습니다.

var xmlString = obj.Serialize();

8
아주 좋은 해결책입니다. 확장 방법으로 이것을 구현 한 방식이 마음에
듭니다

57
여기에 제안 할 한 가지 : try ... catch 블록을 제거하십시오. 그것은 당신에게 어떤 이익도주지 않으며 던져지는 오류를 난독 화합니다.
jammycakes

7
스트링 라이터에서도 사용할 필요가 없습니까? 예 : using (var stringWriter = new StringWriter ())
Steven Quick

3
@jammycakes 아니요! Exception거기 에 새로운 것을 던지면 "Serialize <>"메소드를 사용하여 StackTrace를 확장했습니다.
user11909

1
@ user2190035 확장 방법 내에서 중단되면 스택 추적이 시작됩니까? 시도와 함께 "스택 추적을 연장"하는 것이 필요하지 않은 것 같습니다?
LeRoi

43

다음 함수는 System.Xml 네임 스페이스를 사용하여 XML 저장 함수를 추가하기 위해 모든 개체에 복사 할 수 있습니다.

/// <summary>
/// Saves to an xml file
/// </summary>
/// <param name="FileName">File path of the new xml file</param>
public void Save(string FileName)
{
    using (var writer = new System.IO.StreamWriter(FileName))
    {
        var serializer = new XmlSerializer(this.GetType());
        serializer.Serialize(writer, this);
        writer.Flush();
    }
}

저장된 파일에서 객체를 생성하려면 다음 기능을 추가하고 [ObjectType]을 생성 할 객체 유형으로 바꿉니다.

/// <summary>
/// Load an object from an xml file
/// </summary>
/// <param name="FileName">Xml file name</param>
/// <returns>The object created from the xml file</returns>
public static [ObjectType] Load(string FileName)
{
    using (var stream = System.IO.File.OpenRead(FileName))
    {
        var serializer = new XmlSerializer(typeof([ObjectType]));
        return serializer.Deserialize(stream) as [ObjectType];
    }
}

writer.Flush()using블록에 중복되어 있습니다- writerDispose()방법으로 플러시합니다.
bavaza

6
내 경험에 의하면 사실이 아닙니다. 더 큰 데이터의 경우 using 문은 버퍼가 지워지기 전에 스트림을 삭제합니다. 100 % 명시 적으로 flush를 호출하는 것이 좋습니다.
Ben Gripka

6
writer.Flush ()는 중복되지 않으므로 반드시 있어야합니다. Flush가 없으면 데이터의 일부가 여전히 StreamWriter 버퍼에 있고 파일이 삭제되고 일부 데이터가 누락 될 수 있습니다.
Tomas Kubes

나는 당신의 코드를 매우 좋아합니다 : 짧고 깔끔합니다. 내 문제는 함수를 다른 클래스에 반복해서 복사하는 것입니다. 코드 복제가 아닙니까? 다른 답변은 템플릿 확장 방법이있는 일반 라이브러리를 제안합니다. 어떻게 생각해?
Michael G

33

확장 클래스 :

using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace MyProj.Extensions
{
    public static class XmlExtension
    {
        public static string Serialize<T>(this T value)
        {
            if (value == null) return string.Empty;

            var xmlSerializer = new XmlSerializer(typeof(T));

            using (var stringWriter = new StringWriter())
            {
                using (var xmlWriter = XmlWriter.Create(stringWriter,new XmlWriterSettings{Indent = true}))
                {
                    xmlSerializer.Serialize(xmlWriter, value);
                    return stringWriter.ToString();
                }    
            }
        }
    }
}

용법:

Foo foo = new Foo{MyProperty="I have been serialized"};

string xml = foo.Serialize();

확장 메소드를 보유하고있는 파일에서 확장 메소드를 보유하고있는 네임 스페이스를 참조하면 작동합니다 (제 예에서는 다음과 같습니다 using MyProj.Extensions;)

확장 메소드를 특정 클래스 (예 :)에만 고유하게 만들려면 확장 메소드 FooT인수를 대체 할 수 있습니다 (예 :).

public static string Serialize(this Foo value){...}


31

아래와 같은 함수를 사용하여 모든 객체에서 직렬화 된 XML을 얻을 수 있습니다.

public static bool Serialize<T>(T value, ref string serializeXml)
{
    if (value == null)
    {
        return false;
    }
    try
    {
        XmlSerializer xmlserializer = new XmlSerializer(typeof(T));
        StringWriter stringWriter = new StringWriter();
        XmlWriter writer = XmlWriter.Create(stringWriter);

        xmlserializer.Serialize(writer, value);

        serializeXml = stringWriter.ToString();

        writer.Close();
        return true;
    }
    catch (Exception ex)
    {
        return false;
    }
}

클라이언트에서 이것을 호출 할 수 있습니다.


21

객체를 직렬화하려면 다음을 수행하십시오.

 using (StreamWriter myWriter = new StreamWriter(path, false))
 {
     XmlSerializer mySerializer = new XmlSerializer(typeof(your_object_type));
     mySerializer.Serialize(myWriter, objectToSerialize);
 }

또한 XmlSerializer가 작동하려면 매개 변수가없는 생성자가 필요합니다.


2
이것은 나를 미치게했다. 왜 항상 비어 있는지 알 수 없었습니다. 그런 다음 답변을 읽은 후 매개 변수가없는 생성자가 없다는 것을 깨달았습니다. 감사합니다.
Andy

19

Ben Gripka의 답변으로 시작하겠습니다.

public void Save(string FileName)
{
    using (var writer = new System.IO.StreamWriter(FileName))
    {
        var serializer = new XmlSerializer(this.GetType());
        serializer.Serialize(writer, this);
        writer.Flush();
    }
}

이 코드를 더 일찍 사용했습니다. 그러나 현실은이 솔루션이 약간 문제가 있음을 보여주었습니다. 일반적으로 대부분의 프로그래머는 저장시 설정을 직렬화하고로드시 설정을 직렬화 해제합니다. 이것은 낙관적 시나리오입니다. 어떤 이유로 인해 직렬화에 실패하면 파일이 부분적으로 작성되고 XML 파일이 완료되지 않아 유효하지 않습니다. 결과적으로 XML 역 직렬화가 작동하지 않고 시작시 응용 프로그램이 중단 될 수 있습니다. 파일이 크지 않으면 먼저 객체를 직렬화 MemoryStream한 다음 스트림을 파일에 쓰도록 제안 합니다. 이 경우는 복잡한 사용자 지정 직렬화가있는 경우 특히 중요합니다. 모든 사례를 테스트 할 수는 없습니다.

public void Save(string fileName)
{
    //first serialize the object to memory stream,
    //in case of exception, the original file is not corrupted
    using (MemoryStream ms = new MemoryStream())
    {
        var writer = new System.IO.StreamWriter(ms);    
        var serializer = new XmlSerializer(this.GetType());
        serializer.Serialize(writer, this);
        writer.Flush();

        //if the serialization succeed, rewrite the file.
        File.WriteAllBytes(fileName, ms.ToArray());
    }
}

실제 시나리오에서 역 직렬화는 손상된 직렬화 파일로 계산해야하며, 때때로 발생합니다. Ben Gripka가 제공하는로드 기능이 좋습니다.

public static [ObjectType] Load(string fileName)
{
    using (var stream = System.IO.File.OpenRead(fileName))
    {
        var serializer = new XmlSerializer(typeof([ObjectType]));
        return serializer.Deserialize(stream) as [ObjectType];        
    }    
}

그리고 일부 복구 시나리오로 인해 랩핑 될 수 있습니다. 설정 파일 또는 문제 발생시 삭제할 수있는 기타 파일에 적합합니다.

public static [ObjectType] LoadWithRecovery(string fileName)
{
    try
    {
        return Load(fileName);
    }
    catch(Excetion)
    {
        File.Delete(fileName); //delete corrupted settings file
        return GetFactorySettings();
    }
}

예를 들어 전원 종료와 같이 MemoryStream을 파일에 쓰는 동안 프로세스가 중단 될 수 없습니까?
John Smith

1
네 가능합니다. 설정을 임시 파일에 기록한 다음 원본을 바꾸면 피할 수 있습니다.
Tomas Kubes 2009 년

18

위의 모든 답은 정답입니다. 이것은 가장 간단한 버전입니다.

private string Serialize(Object o)
{
    using (var writer = new StringWriter())
    {
        new XmlSerializer(o.GetType()).Serialize(writer, o);
        return writer.ToString();
    }
}

9

ToString클래스 의 메소드를 호출하는 것보다 조금 더 복잡 하지만 그리 많지는 않습니다.

다음은 모든 유형의 객체를 직렬화하는 데 사용할 수있는 간단한 드롭 인 기능입니다. 직렬화 된 XML 내용을 포함하는 문자열을 반환합니다.

public string SerializeObject(object obj)
{
    System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
    using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) {
        serializer.Serialize(ms, obj);
        ms.Position = 0;
        xmlDoc.Load(ms);
        return xmlDoc.InnerXml;
    }
}


4
    string FilePath = ConfigurationReader.FileLocation;   //Getting path value from web.config            
    XmlSerializer serializer = new XmlSerializer(typeof(Devices)); //typeof(object)
            MemoryStream memStream = new MemoryStream();
            serializer.Serialize(memStream, lstDevices);//lstdevices : I take result as a list.
            FileStream file = new FileStream(folderName + "\\Data.xml", FileMode.Create, FileAccess.ReadWrite); //foldername:Specify the path to store the xml file
            memStream.WriteTo(file);
            file.Close();

원하는 위치에 결과를 xml 파일로 만들어 저장할 수 있습니다.


4

내 작업 코드. utf8 xml enable 빈 네임 스페이스를 리턴 합니다.

// override StringWriter
public class Utf8StringWriter : StringWriter
{
    public override Encoding Encoding => Encoding.UTF8;
}

private string GenerateXmlResponse(Object obj)
{    
    Type t = obj.GetType();

    var xml = "";

    using (StringWriter sww = new Utf8StringWriter())
    {
        using (XmlWriter writer = XmlWriter.Create(sww))
        {
            var ns = new XmlSerializerNamespaces();
            // add empty namespace
            ns.Add("", "");
            XmlSerializer xsSubmit = new XmlSerializer(t);
            xsSubmit.Serialize(writer, obj, ns);
            xml = sww.ToString(); // Your XML
        }
    }
    return xml;
}

예제는 응답 Yandex API 지불 Aviso url을 반환합니다.

<?xml version="1.0" encoding="utf-8"?><paymentAvisoResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" performedDatetime="2017-09-01T16:22:08.9747654+07:00" code="0" shopId="54321" invoiceId="12345" orderSumAmount="10643" />

4

C #을 사용하여 객체를 XML로 직렬화하는 간단한 방법이 있으며 훌륭하게 재사용 가능합니다. 나는 이것이 오래된 스레드라는 것을 알고 있지만 누군가이 도움이 될 수 있기 때문에 이것을 게시하고 싶었습니다.

다음은 메소드 호출 방법입니다.

var objectToSerialize = new MyObject();
var xmlString = objectToSerialize.ToXmlString();

작업을 수행하는 클래스는 다음과 같습니다.

참고 : 이들은 확장 메소드이므로 정적 클래스에 있어야합니다.

using System.IO;
using System.Xml.Serialization;

public static class XmlTools
{
    public static string ToXmlString<T>(this T input)
    {
        using (var writer = new StringWriter())
        {
            input.ToXml(writer);
            return writer.ToString();
        }
    }

    private static void ToXml<T>(this T objectToSerialize, StringWriter writer)
    {
        new XmlSerializer(typeof(T)).Serialize(writer, objectToSerialize);
    }
}

4

위의 솔루션을 기반으로 모든 객체를 직렬화하고 역 직렬화하는 데 사용할 수있는 확장 클래스가 있습니다. 다른 XML 속성은 귀하에게 달려 있습니다.

다음과 같이 사용하십시오.

        string s = new MyObject().Serialize(); // to serialize into a string
        MyObject b = s.Deserialize<MyObject>();// deserialize from a string



internal static class Extensions
{
    public static T Deserialize<T>(this string value)
    {
        var xmlSerializer = new XmlSerializer(typeof(T));

        return (T)xmlSerializer.Deserialize(new StringReader(value));
    }

    public static string Serialize<T>(this T value)
    {
        if (value == null)
            return string.Empty;

        var xmlSerializer = new XmlSerializer(typeof(T));

        using (var stringWriter = new StringWriter())
        {
            using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true }))
            {
                xmlSerializer.Serialize(xmlWriter, value);
                return stringWriter.ToString();
            }
        }
    }
}

2

또는이 방법을 객체에 추가 할 수 있습니다.

    public void Save(string filename)
    {
        var ser = new XmlSerializer(this.GetType());
        using (var stream = new FileStream(filename, FileMode.Create))
            ser.Serialize(stream, this);
    }

1

C # 객체를 xml로 직렬화하는 데 도움이되는 기본 코드는 다음과 같습니다.

using System;

public class clsPerson
{
  public  string FirstName;
  public  string MI;
  public  string LastName;
}

class class1
{ 
   static void Main(string[] args)
   {
      clsPerson p=new clsPerson();
      p.FirstName = "Jeff";
      p.MI = "A";
      p.LastName = "Price";
      System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(p.GetType());
      x.Serialize(Console.Out, p);
      Console.WriteLine();
      Console.ReadLine();
   }
}    

6
이 코드의 출처를 인용하면 좋을 것입니다 : support.microsoft.com/en-us/help/815813/…
MaLiN2223

0
public string ObjectToXML(object input)
{
    try
    {
        var stringwriter = new System.IO.StringWriter();
        var serializer = new XmlSerializer(input.GetType());
        serializer.Serialize(stringwriter, input);
        return stringwriter.ToString();
    }
    catch (Exception ex)
    {
        if (ex.InnerException != null)
            ex = ex.InnerException;

        return "Could not convert: " + ex.Message;
    }
}

//Usage
var res = ObjectToXML(obj)

다음 클래스를 사용해야합니다.

using System.IO;
using System.Xml;
using System.Xml.Serialization;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.