rainit2006 / C-Program

C# Program knowledge
0 stars 0 forks source link

XML, JSON文件处理 #19

Open rainit2006 opened 7 years ago

rainit2006 commented 7 years ago

在程序中访问进而操作XML文件一般有两种模型,分别是使用DOM(文档对象模型)和流模型。

使用DOM的好处在于它允许编辑和更新XML文档,可以随机访问文档中的数据,可以使用XPath查询,但是,DOM的缺点在于它需要一次性的加载整个文档到内存中,对于大型的文档,这会造成资源问题。

流模型很好的解决了这个问题,因为它对XML文件的访问采用的是流的概念,也就是说,任何时候在内存中只有当前节点,但它也有它的不足,它是只读的,仅向前的,不能在文档中执行向后导航操作。

http://www.cnblogs.com/xiaoxiangfeizi/archive/2011/07/29/2120807.html 三种读取xml文件的方法: 1: 使用 XmlDocument 2: 使用 XmlTextReader 3: 使用 Linq to Xml

3,调用SelectSingleNode得到指定的结点,通过GetAttribute得到具体的属性值

// 得到根节点bookstore
XmlNode xn = xmlDoc.SelectSingleNode("bookstore");  

// 得到根节点的所有子节点
XmlNodeList xnl = xn.ChildNodes;

foreach (XmlNode xn1 in xnl)
{
     BookModel bookModel = new BookModel();
     // 将节点转换为元素,便于得到节点的属性值
     XmlElement xe = (XmlElement)xn1;
     // 得到Type和ISBN两个属性的属性值
     bookModel.BookISBN = xe.GetAttribute("ISBN").ToString();
     bookModel.BookType = xe.GetAttribute("Type").ToString();
     // 得到Book节点的所有子节点
     XmlNodeList xnl0 = xe.ChildNodes;
     bookModel.BookName=xnl0.Item(0).InnerText;
     bookModel.BookAuthor=xnl0.Item(1).InnerText;
     bookModel.BookPrice=Convert.ToDouble(xnl0.Item(2).InnerText);
     bookModeList.Add(bookModel);
 }
dgvBookInfo.DataSource = bookModeList;

4,要关掉reader. reader.Close();

同理, ■想增加节点: 调用SelectSingleNode方法获得根结点, 通过CreateElement方法创建元素, 用CreateAttribute创建属性, 用AppendChild把当前结点挂接在其它结点上, 用SetAttributeNode设置结点的属性

■删除某一个数据 想要删除某一个结点,直接找到其父结点,然后调用RemoveChild方法即可。 现在关键的问题是如何找到这个结点,上面的SelectSingleNode可以传入一个Xpath表、我们通过书的ISBN号来找到这本书所在的结点.如下:

   XmlElement xe = xmlDoc.DocumentElement; 
// DocumentElement 获取xml文档对象的根XmlElement.

   string strPath = string.Format("/bookstore/book[@ISBN=\"{0}\"]",    dgvBookInfo.CurrentRow.Cells[1].Value.ToString());
   XmlElement selectXe = (XmlElement)xe.SelectSingleNode(strPath);  
//selectSingleNode 根据XPath表达式,获得符合条件的第一个节点.

   selectXe.ParentNode.RemoveChild(selectXe);

"/bookstore/book[@ISBN=\"{0}\"]"是一个Xpath表达式,找到ISBN号为所选那一行ISBN号的那本书

■修改某要条数据 用Xpath表达式找到所需要修改的那一个结点,然后如果是元素的话,就直接对这个元素赋值,如果是属性的话,就用SetAttribute方法设置即可

XmlTextReader:首先创建一个流,然后用read()方法来不断的向下读,根据读取的结点的类型来进行相应的操作。

   XmlTextReader reader = new XmlTextReader(@"..\..\Book.xml");
              List<BookModel> modelList = new List<BookModel>();
              BookModel model = new BookModel();
              while (reader.Read())
              {

                  if (reader.NodeType == XmlNodeType.Element)
                  {
                      if (reader.Name == "book")
                      {
                          model.BookType = reader.GetAttribute(0);
                          model.BookISBN = reader.GetAttribute(1);
                      }
                      if (reader.Name == "title")
                      {
                          model.BookName=reader.ReadElementString().Trim();
                      }
                      if (reader.Name == "author")
                      {
                          model.BookAuthor = reader.ReadElementString().Trim();
                      }
                      if (reader.Name == "price")
                      {
                          model.BookPrice = Convert.ToDouble(reader.ReadElementString().Trim());
                      }
                  }

                  if (reader.NodeType == XmlNodeType.EndElement)
                  {
                      modelList.Add(model);
                      model = new BookModel();
                  }            
              }
            modelList.RemoveAt(modelList.Count-1);
           this.dgvBookInfo.DataSource = modelList;

关键是: 读取属性的时候,你要先知道哪一个结点具有几个属性,然后通过GetAttribute方法来读取.

读取属性还可以用另外一种方法,就是用MoveToAttribute方法.

 if (reader.Name == "book")
       {
           for (int i = 0; i < reader.AttributeCount; i++)
           {
               reader.MoveToAttribute(i);
               string str = "属性:" + reader.Name + "=" + reader.Value;
           }
           model.BookType = reader.GetAttribute(0);
           model.BookISBN = reader.GetAttribute(1);
       }

XmlTextWriter: 写文件的时候,默认是覆盖以前的文件,如果此文件名不存在,它将创建此文件. 1、首先设置一下,你要创建的XML文件格式,

XmlTextWriter myXmlTextWriter = new XmlTextWriter(@"..\..\Book1.xml", null);
   //使用 Formatting 属性指定希望将 XML 设定为何种格式。 这样,子元素就可以通过使用 Indentation 和 IndentChar 属性来缩进。
   myXmlTextWriter.Formatting = Formatting.Indented;

2、然后可以通过WriteStartElement和WriteElementString方法来创建元素, 这两者的区别就是如果有子结点的元素,那么创建的时候就用WriteStartElement,然后去创建子元素,创建完毕后,要调用相应的WriteEndElement来告诉编译器,创建完毕, 用WriteElementString来创建单个的元素, 用WriteAttributeString来创建属性.

2, 读取所有的数据: 直接找到元素为book的这个结点,然后遍历读取所有的结果.
private void btnReadAll_Click(object sender, EventArgs e)
       {
           XElement xe = XElement.Load(@"..\..\Book.xml");
           IEnumerable<XElement> elements = from ele in xe.Elements("book")
                                            select ele;
           showInfoByElements(elements);
       }

3.2插入一条数据: 插入结点和属性都采用new的方法

private void btnInsert_Click(object sender, EventArgs e) { XElement xe = XElement.Load(@"....\Book.xml"); XElement record = new XElement( new XElement("book", new XAttribute("Type", "选修课"), new XAttribute("ISBN","7-111-19149-1"), new XElement("title", "计算机操作系统"), new XElement("author", "7-111-19149-1"), new XElement("price", 28.00))); xe.Add(record); xe.Save(@"....\Book.xml"); MessageBox.Show("插入成功!"); btnReadAll_Click(sender, e); }


3.3 删除选中的数据
首先得到选中的那一行,通过ISBN号来找到这个元素,然后用Remove方法直接删除

private void btnDelete_Click(object sender, EventArgs e) { if (dgvBookInfo.CurrentRow != null) { //dgvBookInfo.CurrentRow.Cells[1]对应着ISBN号 string id = dgvBookInfo.CurrentRow.Cells[1].Value.ToString(); XElement xe = XElement.Load(@"....\Book.xml"); IEnumerable elements = from ele in xe.Elements("book") where (string)ele.Attribute("ISBN") == id select ele; { if (elements.Count() > 0) elements.First().Remove(); } xe.Save(@"....\Book.xml"); MessageBox.Show("删除成功!"); btnReadAll_Click(sender, e);

          }
      }


3.4 删除所有的数据
与上面的类似,选出所有的数据,然后用Remove方法
rainit2006 commented 7 years ago

XMLファイルに書き込まないメンバを指定する 書き込みたくないメンバにXmlIgnoreAttribute属性をつけます。

public class SampleClass
{
    public int Number;
    //このメンバの値はシリアル化しないようにする
    [System.Xml.Serialization.XmlIgnore]
    public string Message;
}

XmlAttribute等属性的使用 http://zero-config.com/dotnet/xmlserializer001.html

rainit2006 commented 7 years ago

XmlReader .NETでのXMLの読み出しには、System.Xml名前空間のクラスを用います。 http://devlights.hatenablog.com/entry/20070117/p1

rainit2006 commented 7 years ago

オブジェクトの内容をXMLファイルに保存(シリアル化)する

   ```

//XmlSerializerオブジェクトを作成 //オブジェクトの型を指定する System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(SampleClass)); //書き込むファイルを開く(UTF-8 BOM無し) System.IO.StreamWriter sw = new System.IO.StreamWriter( fileName, false, new System.Text.UTF8Encoding(false)); //シリアル化し、XMLファイルに保存する serializer.Serialize(sw, obj);


XMLファイルの内容をオブジェクトに復元(逆シリアル化)する
 //XmlSerializerオブジェクトを作成
    System.Xml.Serialization.XmlSerializer serializer =
        new System.Xml.Serialization.XmlSerializer(typeof(SampleClass));
    //読み込むファイルを開く
    System.IO.StreamReader sr = new System.IO.StreamReader(
        fileName, new System.Text.UTF8Encoding(false));
    //XMLファイルから読み込み、逆シリアル化する
    SampleClass obj = (SampleClass)serializer.Deserialize(sr);

配列をシリアル化、逆シリアル化する

//保存する配列を作成 SampleClass[] ary = new SampleClass[2]; for (int i = 0; i < ary.Length; i++) { ary[i] = new SampleClass(); ary[i].Number = i; ary[i].Message = i.ToString() + "です。"; }

//XMLファイルに保存する System.Xml.Serialization.XmlSerializer serializer1 = new System.Xml.Serialization.XmlSerializer(typeof(SampleClass[])); System.IO.StreamWriter sw = new System.IO.StreamWriter( @"C:\test\sample.xml", false, new System.Text.UTF8Encoding(false)); serializer1.Serialize(sw, ary); sw.Close();

//保存した内容を復元する System.Xml.Serialization.XmlSerializer serializer2 = new System.Xml.Serialization.XmlSerializer(typeof(SampleClass[])); System.IO.StreamReader sr = new System.IO.StreamReader( @"C:\test\sample.xml", new System.Text.UTF8Encoding(false)); SampleClass[] loadAry; loadAry = (SampleClass[])serializer2.Deserialize(sr);

rainit2006 commented 7 years ago

C#でJSONを扱う方法

http://dev.classmethod.jp/etc/c-sharp-json/ C#でJSONを扱う方法はいろいろありますが、 ■ DataContractJsonSerializer ■ Json.NET ■ DynamicJson あたりがよく知られたメジャーな方法だ

rainit2006 commented 6 years ago

Serializable

[Serializable]
public class MyObject {
  public int n1 = 0;
  public int n2 = 0;
  public String str = null;
}

//The code example below shows how an instance of this class can be serialized to a file.
MyObject obj = new MyObject();
obj.n1 = 1;
obj.n2 = 24;
obj.str = "Some String";
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("MyFile.bin", FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, obj);
stream.Close();

//Restoring the object back to its former state is just as easy. 
///First, create a stream for reading and a formatter, 
///and then instruct the formatter to deserialize the object. 
///The code example below shows how this is done.
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("MyFile.bin", FileMode.Open, FileAccess.Read, FileShare.Read);
MyObject obj = (MyObject) formatter.Deserialize(stream);
stream.Close();

// Here's the proof.
Console.WriteLine("n1: {0}", obj.n1);
Console.WriteLine("n2: {0}", obj.n2);
Console.WriteLine("str: {0}", obj.str);

オブジェクトのXMLシリアル化、逆シリアル化

//XMLファイルに保存するオブジェクトのためのクラス
public class SampleClass
{
    public int Number;
    public string Message;
}

class MainClass
{
    //SampleClassオブジェクトをXMLファイルに保存する
    public static void Main()
    {
        //保存先のファイル名
        string fileName = @"C:\test\sample.xml";

        //保存するクラス(SampleClass)のインスタンスを作成
        SampleClass obj = new SampleClass();
        obj.Message = "テストです。";
        obj.Number = 123;

        //XmlSerializerオブジェクトを作成
        //オブジェクトの型を指定する
        System.Xml.Serialization.XmlSerializer serializer =
            new System.Xml.Serialization.XmlSerializer(typeof(SampleClass));
        //書き込むファイルを開く(UTF-8 BOM無し)
        System.IO.StreamWriter sw = new System.IO.StreamWriter(
            fileName, false, new System.Text.UTF8Encoding(false));
        //シリアル化し、XMLファイルに保存する
        serializer.Serialize(sw, obj);
        //ファイルを閉じる
        sw.Close();
    }
}

///XMLファイルの内容をオブジェクトに復元(逆シリアル化)する
class MainClass
{
    //XMLファイルをSampleClassオブジェクトに復元する
    public static void Main()
    {
        //保存元のファイル名
        string fileName = @"C:\test\sample.xml";

        //XmlSerializerオブジェクトを作成
        System.Xml.Serialization.XmlSerializer serializer =
            new System.Xml.Serialization.XmlSerializer(typeof(SampleClass));
        //読み込むファイルを開く
        System.IO.StreamReader sr = new System.IO.StreamReader(
            fileName, new System.Text.UTF8Encoding(false));
        //XMLファイルから読み込み、逆シリアル化する
        SampleClass obj = (SampleClass)serializer.Deserialize(sr);
        //ファイルを閉じる
        sr.Close();
    }
}

XML属性、テキストとして保存する メンバをXML属性として保存するにはXmlAttributeAttributeを、XMLテキストとして保存するにはXmlTextAttributeを使います。

//XMLファイルに保存するオブジェクトのためのクラス
[System.Xml.Serialization.XmlRoot("サンプル")]
public class SampleClass
{
    [System.Xml.Serialization.XmlText]
    public int Number;
    [System.Xml.Serialization.XmlAttribute("文字列")]
    public string Message;
}

結果:
<?xml version="1.0" encoding="utf-8"?>
<サンプル xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 文字列="テストです。">123</サンプル>
rainit2006 commented 6 years ago

DataContractSerializerを使って、オブジェクトのXMLシリアル化、逆シリアル化を行う

DataContractSerializerクラスを使うためには参照設定に「System.Runtime.Serialization.dll」が必要. TestClassクラスには、DataContractAttribute属性を適用します。 シリアル化したいメンバ(フィールドやプロパティ)には、DataMemberAttribute属性を適用します。DataMemberAttribute属性はパブリックメンバだけでなく、プライベートメンバにも適用できます。DataMemberAttribute属性が適用されていないメンバは、シリアル化されません。

using System.Runtime.Serialization;
using System.Xml;

//XMLファイルに保存するオブジェクトのためのクラス
[DataContract]
public class TestClass
{
    [DataMember]
    public int Number;
    //DataMemberAttributeが無いのでシリアル化されない
    public string Message;
    //プライベートでもDataMemberAttributeがあればシリアル化される
    [DataMember]
    private string PrivateMessage;

    //コンストラクタ
    public TestClass()
    {
        this.Number = 10;
        this.Message = "こんにちは。";
        this.PrivateMessage = "はじめまして。";
    }

    //コンストラクタ
    public TestClass(int num, string msg, string pmsg)
    {
        this.Number = num;
        this.Message = msg;
        this.PrivateMessage = pmsg;
    }
}

public class Program
{
    //TestClassオブジェクトをXMLファイルに保存する
    static void Main(string[] args)
    {
        //保存先のファイル名
        string fileName = @"C:\test\test.xml";

        //保存するクラス(TestClass)のインスタンスを作成
        TestClass obj = new TestClass();

        //DataContractSerializerオブジェクトを作成
        //オブジェクトの型を指定する
        DataContractSerializer serializer =
            new DataContractSerializer(typeof(TestClass));
        //BOMが付かないUTF-8で、書き込むファイルを開く
        XmlWriterSettings settings = new XmlWriterSettings();
        settings.Encoding = new System.Text.UTF8Encoding(false);
        XmlWriter xw = XmlWriter.Create(fileName, settings);
        //シリアル化し、XMLファイルに保存する
        serializer.WriteObject(xw, obj);
        //ファイルを閉じる
        xw.Close();
    }
}

//保存されるXMLファイルの中身は、以下
<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/">
<Number>10</Number>
<PrivateMessage>はじめまして。</PrivateMessage>
</TestClass>

補足:上のコードではXmlWriterを使用していますが、代わりにFileStreamのみを使用して書き込みをした時は、XML宣言(上の例では、「<?xml version="1.0" encoding="utf-8"?>」)が省略され、UTF-8で書き込まれます。

また、メンバにDataMemberAttribute.Orderプロパティを指定すると、これが指定されていないメンバの後の順番になります。そして、Orderプロパティに指定された数字の順番に並びます。Orderの値が同じ時は、アルファベット順です。

[DataContract]
public class BaseType
{

    [DataMember]
    public string zebra;
}
[DataContract]
public class DerivedType : BaseType
{
    [DataMember(Order = 0)]
    public string bird;
    [DataMember(Order = 1)]
    public string parrot;
    [DataMember]
    public string dog;
    [DataMember(Order = 3)]
    public string antelope;
    [DataMember]
    public string cat;
    [DataMember(Order = 1)]
    public string albatross;
}

結果:
<DerivedType>
    <!-- Zebra is a base data member, and appears first. -->
    <zebra/> 

    <!-- Cat has no Order, appears alphabetically first. -->
    <cat/>

   <!-- Dog has no Order, appears alphabetically last. -->
    <dog/> 

    <!-- Bird is the member with the smallest Order value -->
    <bird/>

    <!-- Albatross has the next Order value, alphabetically first. -->
    <albatross/>

    <!-- Parrot, with the next Order value, alphabetically last. -->
     <parrot/>

    <!-- Antelope is the member with the highest Order value. Note that 
    Order=2 is skipped -->
     <antelope/> 
</DerivedType>

結果: <?xml version="1.0" encoding="utf-8"?> <サンプル xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/"> <数字>10</数字> <文字列>こんにちは。</文字列> </サンプル>



- 名前空間を変更する
名前空間を変更するには、DataContractAttribute.Namespaceプロパティを指定します。名前空間を使わないようにするには、Namespaceプロパティに空白文字列を指定します。
名前空間が同じでないと逆シリアル化できません。
rainit2006 commented 6 years ago

XmlSerializerとDataContractSerializerの比較

rainit2006 commented 6 years ago

Use the XmlIncludeAttribute when you call the Serialize or Deserialize method of the XmlSerializer class. When applying the XmlIncludeAttribute, specify the Type of the derived class. When the XmlSerializer serializes objects that include both the base and the derived class, it can then recognize both object types.

[DataContract] 
class Foo {}  
[DataContract] 
class Bar : Foo {}

with a method on the WCF interface that returns: public Foo GetFoo() { return new Bar(); } Without the attribute, the serializer (especially for mex/proxy-generated types) won't know about Bar, and it will fail. With the attribute:

[DataContract, KnownType(typeof(Bar))] 
class Foo {}

it will work.

This only applies to DataContractSerializer - with NetDataContractSerializer you get the type data in a different way.

rainit2006 commented 5 years ago

C++ 用picojson来解析json文件

#include <picojson.h>
#include <iostream> // std::cout
#include <fstream>  // std::ifstream

std::ifstream fs;
    CString strfilePath = FilePath + fileName;
    fs.open(strfilePath, std::ios::binary);
    if (fs)
    {
        fileInfo_first.fileName = fileName;

        picojson::value val;
        fs >> val;

        picojson::object& Object = val.get<picojson::object>();
        picojson::object& objData = Object["data"].get<picojson::object>();

        try
        {
            CString Type(val.get<picojson::object>()
                ["data"].get<picojson::object>()
                ["Type"].get<std::string>().c_str());
            fileInfo_first.fileType = Type;
        }
        catch (exception exp)
        {
            fileInfo_first.fileType = "";
        }

        try 
        {
            CString Title(val.get<picojson::object>()
                ["data"].get<picojson::object>()
                ["Title"].get<std::string>().c_str());
            fileInfo_first.fileTitle = Title;

        }
        catch (exception exp) 
        {
            fileInfo_first.fileTitle = "";
        }

        try
        {
            CString Summary(val.get<picojson::object>()
                ["data"].get<picojson::object>()
                ["Summary"].get<std::string>().c_str());
            fileInfo_first.fileSummary = Summary;
        }
        catch (exception exp)
        {
            fileInfo_first.fileSummary = "";
        }
        m_FileList.push_back(fileInfo_first);
        fs.close();
    }

这里json数据格式:

{
  "data":
  {
    "Name":"name3",
    "Type":"type1",
    "Title":"XXXTitle",
    "Summary":"XXXSummary",
    "Content":
    [.....]
 }
}