[C#] LINQ를 이용한 XML 파싱 쿼리

“XML에서 원하는 데이터 검색하기”에서 XML을 쿼리하는 방법을 학습한 뒤에 LINQ를 이용해 XML을 쿼리 하는 방법도 학습해 보았습니다. “XML에서 원하는 데이터 검색하기”에서 사용한 동일한 XML 데이터를 이용했고.. 그 쿼리 대상도 동일하게 했습니다. 다시 한번 쿼리 대상을 살펴보면…

  1. <book>의 개수는?
  2. 두번째 <book>의 <title>의 값은?
  3. 두번째 <book>의 genre의 값은?
  4. 두번째 <book>의 <author>의 <first-name>의 값은?

위에 대한 결과는 다음과 같습니다.

  1. 3
  2. The Confidence Man
  3. novel
  4. Herman

Linq를 이용해 위의 쿼리 대상의 결과를 얻어 내는 코드를 하나 하나 들어 보기에 앞서 LINQ를 XML에 적용하기 위한 준비 코드가 필요하며 다음과 같습니다.

XmlTextReader reader = new XmlTextReader("books.xml");
XElement xml = XElement.Load(reader);

var books = from item in xml.Descendants("book") select item;

여기서 4번 코드가 바로 LINQ입니다. 사실.. XML에서 원하는 데이터를 쿼리 하는 기능은 LINQ의 기능중 작은 부분을 차지 하고 있어 그리 크게 느껴지지 않습니다.

뭐 여튼…… 이제 우리의 목표를 하나 하나 이뤄보겠습니다. 먼저 “<book>의 개수는?”에 대한 코드입니다.

Console.WriteLine(books.Count());

다음은 “두번째 <book>의 <title>의 값은?”에 대한 코드입니다.

Console.WriteLine(
    books.ElementAt(1).Descendants("title").ElementAt(0).Value
);

그리고 세번째로 “두번째 <book>의 genre의 값은?”에 대한 코드입니다.

Console.WriteLine(books.ElementAt(1).Attribute("genre").Value);

끝으로 “두번째  <book>의 <author>의 <first-name>의 값은?”에 대한 코드입니다.

Console.WriteLine(
    books.ElementAt(1).Descendants("author").Descendants("first-name")
        .ElementAt(0).Value
);

제가 LINQ를 제대로 이해하고 있지 않은 이유일까요? LINQ를 이용해 XML에서 원하는 데이터를 쿼리하는 간결하고 직관적인 코드가 존재하지 않을까… 하는 생각이 듭니다. 바로 이런 이유가 LINQ의 이유 중에 하나이니까요… 앞서 언급했지만.. XML에서 원하는 데이터를 쿼리 하는 기능은 LINQ의 기능중 작은 부분을 차지 하고 있는듯하여 LINQ를 통해 XML을 쿼리하는 방식은 그리 매력적이라고 느껴지지 않습니다..

[C#] XML에서 원하는 데이터 검색하기

C#에서 XML 데이터를 처리해야할 기능이 필요하여 이리 저리 찾은 내용을 정리한 글입니다. 먼저 사용할 XML 데이터는 아래와 같다고 하겠습니다.


    
        
            The Autobiography of Benjamin Franklin
            
                Benjamin
                Franklin
            
            8.99
        
        
            The Confidence Man
            
                Herman
                Melville
            
            11.99
        
        
            The Gorgias
            
                Plato
            
            9.99
        

가장 먼저 위의 XML 데이터를 쉽게 조회하기 위해 트리 형태로 구성하기 위한 준비 코드는 다음과 같습니다.

XmlTextReader reader = new XmlTextReader("books.xml");
XmlDocument xdoc = new XmlDocument();
xdoc.Load(reader);

자.. 이제 부터 원하는 데이터를 얻어 낼 수 있는데요. 다음과 같이 네 가지의 목표를 정하고 목표를 이루어 나가 보겠습니다.

  1. 은 몇개인가?
  2. 두번째 의의 값은 무엇인가?
  3. 두번째 의 속성 genre의 값은 무엇인가?
  4. 두번째 의 의 의 값은 무엇인가?

먼저 답은 다음과 같습니다.

  1. 3개
  2. The Confidence Man
  3. novel
  4. Herman

위의 답을 내어 내기 위한 코드를 하나 하나 제시해 보면 다음과 같습니다.

“은 몇개인가?”에 대한 코드

XmlNodeList bookNodes = xdoc.DocumentElement.SelectNodes("book");
Console.WriteLine(" 노드의 개수: " + bookNodes.Count);

“두번째 의의 값은 무엇인가?”에 대한 코드

XmlNode titleNode = bookNodes[1].SelectSingleNode("title");
Console.WriteLine("두번째 의의 값은: " + titleNode.InnerText);

“두번째 의 속성 genre의 값은 무엇인가?”에 대한 코드

String genre = bookNodes[1].Attributes["genre"].Value;
Console.WriteLine(genre);

끝으로.. “두번째 의 의 의 값은 무엇인가?”에 대한 코드

String firstName = bookNodes[1].SelectSingleNode("author")
    .SelectSingleNode("first-name").InnerText;
Console.WriteLine(firstName);

끝으로 이보다 효율적인 방법이 존재하겠지요? 알고 계시다면 한수 부탁드립니다!

[GIS] 자바 API를 이용한 ArcSDE 9.2 연동

환경은 ArcSDE 9.2이고..  ArcSDE 서비스 팩은 설치하지 않았으며 오라클은 10g 버전입니다. 그리고 ArcSDE에 대한 자바 API는 ArcSDE 9.2 서비스팩6에 해당합니다. 참고로 자바 API를 구성하는 jar 파일은 다음과 같으며 모두 반드시.. 필요합니다.

  • jsde92_sdk.jar
  • jpe92_sdk.jar
  • icu4j_3_2.jar

네.. 이제 ArcSDE와 연동하기 위한 자바 API에 대해 살펴보겠습니다.. 먼저 연결에 대한 코드입니다..

import java.util.*;
import com.esri.sde.sdk.client.*;

public class MainEntry {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        SeConnection conn = null;
        String server = "192.168.0.18";
        int service = 5151;
        String database = "";
        String user = "na_gis";
        String password = "na_gis";

        try {
            conn = new SeConnection(server, service, database, user, password);
            Vector layerList = conn.getLayers();
            System.out.println(layerList.size() + " layers existed... ");

            for(int i=0; i

위의 코드는 연결뿐만 아니라 연결된 ArcSDE 서버가 관리하고 있는 레이어 개수와 이름을 화면에 출력합니다.

다음은 레이어를 구성하는 필드 정보를 얻어 오는 코드입니다.

import java.util.*;
import com.esri.sde.sdk.client.*;

public class MainEntry {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        SeConnection conn = null;
        String server = "192.168.0.18";
        int service = 5151;
        String database = "";
        String user = "na_gis";
        String password = "na_gis";

        try {
            conn = new SeConnection(server, service, database, user, password);

            Vector tableList = conn.getTables(SeDefs.SE_SELECT_PRIVILEGE);
            System.out.println(tableList.size() + " table existed... ");
   
            for(int i=0; i

필드 정보는 레이어 개념이 아닌 테이블 개념으로 가져와야 합니다. 레이어는 테이블 + 도형 데이터 쯤으로.. 생각하면 됩니다.

다음은 테이블의 특정 필드(들)의 값을 얻어오는 코드입니다.

import java.util.*;
import com.esri.sde.sdk.client.*;

public class MainEntry {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        SeConnection conn = null;
        String server = "192.168.0.18";
        int service = 5151;
        String database = "";
        String user = "na_gis";
        String password = "na_gis";

        try {
            conn = new SeConnection(server, service, database, user, password);
            Vector tableList = conn.getTables(SeDefs.SE_SELECT_PRIVILEGE);

            SeTable tbl = (SeTable)tableList.get(89);

            SeSqlConstruct sqlConstruct = 
                new SeSqlConstruct(tbl.getQualifiedName());

            String[] cols = new String[1];
            cols[0] = "IST_YMD";

            SeQuery query = new SeQuery(conn, cols, sqlConstruct);
            query.prepareQuery();
            query.execute();

            SeRow row;
            while((row = query.fetch()) != null)
            {
                String value = row.getString(0);
                System.out.println(value);
            }
        } catch(SeException e) {
            e.printStackTrace();
        }
    }
}

얻어온 테이블 리스트(Vector tableList)에서 인덱스 번호 89에 해당하는 테이블의 IST_YMD 필드에 대한 값을 뽑아 내 화면에 표시합니다. 만약 SQL의 Where 구문을 지정하고자 한다면 SeSqlConstruct sqlConstruct 객체의 매서드 setWhere에 구문을 지정하면 됩니다. 예를 들면.. 만약 필드 ADM_NAM에 대해서 '목'으로 시작하는 조건의 경우는 다음과 같습니다.

    ....

    sqlConstruct.setWhere("ADM_NAM LIKE '목%'");

    ....

다음은 레이어에서 도형에 대한 좌표를 표시하는 코드입니다.

import java.util.*;
import com.esri.sde.sdk.client.*;

public class MainEntry {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        SeConnection conn = null;
        String server = "192.168.0.18";
        int service = 5151;
        String database = "";
        String user = "na_gis";
        String password = "na_gis";

        try {
            conn = new SeConnection(server, service, database, user, password);
            Vector layerList = conn.getLayers();

            SeLayer lyr = (SeLayer)layerList.get(4);
            SeSqlConstruct sqlConstruct =
                new SeSqlConstruct(lyr.getQualifiedName());

            String[] cols = new String[3];
            cols[0] = "BD_NM";
            cols[1] = "BD_MGT_SN";
            cols[2] = "SHAPE";

            SeQuery query = new SeQuery(conn, cols, sqlConstruct);
            query.prepareQuery();
            query.execute();

            SeRow row;
            while((row = query.fetch()) != null)
            {
                SeShape shp = row.getShape(2);

                double coordinates[][][] = shp.getAllCoords();
                System.out.println("coordinates.length = " + coordinates.length);
                System.out.println("coordinates[0].length = " + coordinates[0].length);
                System.out.println("coordinates[0][0].length = " + coordinates[0][0].length);

                double partCoords[] = coordinates[0][0];
                for(int i=0; i

얻어온 레이어 리스트(Vector layerList)에서 인덱스 4번에 해당하는 레이어에 대해서 필드 BD_NM, BD_MGT_SN 그리고 좌표에 대한 값이 담긴 SHPAE 필드를 지정해 쿼리를 실행합니다. 위의 코드는 지정된 레이어에 대한 모든 레코드를 읽어 오게 됩니다. 여기에 제한을 두어 지정된 MBR에 포함되거나 교차하는 레코드만을 얻어오는 코드는 다음과 같습니다.

import java.util.*;
import com.esri.sde.sdk.client.*; public class MainEntry {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        SeConnection conn = null;
        String server = "192.168.0.18";
        int service = 5151;
        String database = "";
        String user = "na_gis";
        String password = "na_gis";

        try {
            conn = new SeConnection(server, service, database, user, password);
            Vector layerList = conn.getLayers();

            SeLayer lyr = (SeLayer)layerList.get(4);
            SeSqlConstruct sqlConstruct = 
                new SeSqlConstruct(lyr.getQualifiedName());

            String[] cols = new String[3];
            cols[0] = "BD_NM";
            cols[1] = "BD_MGT_SN";
            cols[2] = "SHAPE";

            SeQuery query = new SeQuery(conn, cols, sqlConstruct);
            query.prepareQuery();

            // Add New Codes
            SeShape filterShape = new SeShape(lyr.getCoordRef());
            SeExtent ext = new SeExtent(148574, 146403, 148587, 146412);
            filterShape.generateRectangle(ext); 

            SeShapeFilter filter = new SeShapeFilter(lyr.getQualifiedName(), 
                lyr.getSpatialColumn(), filterShape, SeShapeFilter.METHOD_AI);

            SeShapeFilter[] filters = new SeShapeFilter[1];
            filters[0] = filter;

            query.setSpatialConstraints(SeQuery.SE_SPATIAL_FIRST, 
                false, filters);
            // At Here!

            query.execute();
   
            SeRow row;
            while((row = query.fetch()) != null)
            {
                SeShape shp = row.getShape(2);

                double coordinates[][][] = shp.getAllCoords();
                System.out.println(
                    "coordinates.length = " + coordinates.length);
                System.out.println(
                    "coordinates[0].length = " + coordinates[0].length);
                System.out.println(
                    "coordinates[0][0].length = " + coordinates[0][0].length);

                double partCoords[] = coordinates[0][0];
                for(int i=0; i

코드 번호 29~42에 해당하는 새로운 코드가 추가되었습니다.

완전하 SELECT 구문에 대한 SQL 문으로 Row을 읽어 오는 코드는 다음과 같습니다.

import java.util.*;   
import com.esri.sde.sdk.client.*; 

public class TestArcSDEQuery {
    public static void main(String[] args) {
        SeConnection conn = null;   
        String server = "222.237.78.208";   
        int service = 5151;   
        String database = "";   
        String user = "na_gis";   
        String password = "na_gis";   

        try {
            conn = new SeConnection(server, service, database, user, password); 
         
            SeQuery query = new SeQuery(conn);   
            query.prepareSql("SELECT * FROM CC_META");
            query.execute();   
  
            SeRow row;
            while((row = query.fetch()) != null)   
            {
                int cntFields = query.getNumColumns();
                SeColumnDefinition[] tableDef = row.getColumns();
             
                for(int iField=0; iField
	

[ActionScript] 서버로 Binary 데이터 전송

액션스크립트에서 바이너리 데이터를 서버로 전송하는 코드입니다. 물론… 서버 측에서 바이너리 데이터를 받아 들이는 서비스를 제공해야겠지요.. 바이너리 데이터 전송이므로.. 방식은 POST를 사용합니다.. 액션스크립트 코드는 다음과 같습니다..

protected function button1_clickHandler(event:MouseEvent):void
{
    var loader:URLLoader = new URLLoader();
    var request:URLRequest = new URLRequest("http://127.0.0.1:8080/Xr?edChkLog");

    var bytes:ByteArray = new ByteArray();
    bytes.writeDouble(3.14);
    bytes.writeInt(34321);
    bytes.position = 0;
   
    request.data = bytes;
    request.method = URLRequestMethod.POST;
    request.contentType = "application/octet-stream";

    loader.dataFormat = URLLoaderDataFormat.BINARY;
    loader.addEventListener(Event.COMPLETE, onCompleted);
    loader.addEventListener(IOErrorEvent.IO_ERROR, onError);

    loader.load(request);
}

바이너리 데이터는 ByteArray 타입에 저장되며.. 원하는 데이터를 write 매서드 군으로 저장시킵니다. 9번 코드와 15번 코드가 꼭 필요한지는 제거해서 확인해 보시기 바랍니다..

서버측은.. 저 같은 경우  자바의 서블릿을 사용했는데… POST 방식으로 요청을 받으므로 doPost 매서드를 오버라이드해서 데이터를 읽어오면 됩니다.. 바이너리 데이터를 쉽게 읽기 위해 DataInputStream을 사용했는데.. 이해를 돕고자.. 관련된 코드를 제시한다면 다음과 같습니다..

    ....

    ServletInputStream in = req.getInputStream();
    System.out.println("readable size: " + in.available());

    DataInputStream dis = new DataInputStream(in);
    System.out.println(dis.readDouble() + " , " + dis.readInt());

    ....

req는 서블릿의 요청에 대한 HttpServletRequest 객체입니다.. 화면상에 읽을 수 있는 바이너리의 바이트 수와 클라이언트가 보낸 실수값(3.14)와 정수값(34321)이 찍히면 정상입니다..

플래시의 액션스크립트를 이용해 서버측으로부터 바이너리 데이터를 읽을 수도.. 또.. 전송할 수 있도 있음으로 퍼포먼스를 향상 시킬 수 있겠습니다..